home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkTextDisp.c < prev    next >
C/C++ Source or Header  |  1995-06-28  |  143KB  |  4,678 lines

  1. /* 
  2.  * tkTextDisp.c --
  3.  *
  4.  *    This module provides facilities to display text widgets.  It is
  5.  *    the only place where information is kept about the screen layout
  6.  *    of text widgets.
  7.  *
  8.  * Copyright (c) 1992-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  */
  14.  
  15. static char sccsid[] = "@(#) tkTextDisp.c 1.91 95/06/28 15:06:50";
  16.  
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19. #include "tkText.h"
  20.  
  21. /*
  22.  * The following structure describes how to display a range of characters.
  23.  * The information is generated by scanning all of the tags associated
  24.  * with the characters and combining that with default information for
  25.  * the overall widget.  These structures form the hash keys for
  26.  * dInfoPtr->styleTable.
  27.  */
  28.  
  29. typedef struct StyleValues {
  30.     Tk_3DBorder border;        /* Used for drawing background under text.
  31.                  * NULL means use widget background. */
  32.     int borderWidth;        /* Width of 3-D border for background. */
  33.     int relief;            /* 3-D relief for background. */
  34.     Pixmap bgStipple;        /* Stipple bitmap for background.  None
  35.                  * means draw solid. */
  36.     XColor *fgColor;        /* Foreground color for text. */
  37.     XFontStruct *fontPtr;    /* Font for displaying text. */
  38.     Pixmap fgStipple;        /* Stipple bitmap for text and other
  39.                  * foreground stuff.   None means draw
  40.                  * solid.*/
  41.     int justify;        /* Justification style for text. */
  42.     int lMargin1;        /* Left margin, in pixels, for first display
  43.                  * line of each text line. */
  44.     int lMargin2;        /* Left margin, in pixels, for second and
  45.                  * later display lines of each text line. */
  46.     int offset;            /* Offset in pixels of baseline, relative to
  47.                  * baseline of line. */
  48.     int overstrike;        /* Non-zero means draw overstrike through
  49.                  * text. */
  50.     int rMargin;        /* Right margin, in pixels. */
  51.     int spacing1;        /* Spacing above first dline in text line. */
  52.     int spacing2;        /* Spacing between lines of dline. */
  53.     int spacing3;        /* Spacing below last dline in text line. */
  54.     TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
  55.                  * be NULL). */
  56.     int underline;        /* Non-zero means draw underline underneath
  57.                  * text. */
  58.     Tk_Uid wrapMode;        /* How to handle wrap-around for this tag.
  59.                  * One of tkTextCharUid, tkTextNoneUid,
  60.                  * or tkTextWordUid. */
  61. } StyleValues;
  62.  
  63. /*
  64.  * The following structure extends the StyleValues structure above with
  65.  * graphics contexts used to actually draw the characters.  The entries
  66.  * in dInfoPtr->styleTable point to structures of this type.
  67.  */
  68.  
  69. typedef struct Style {
  70.     int refCount;        /* Number of times this structure is
  71.                  * referenced in Chunks. */
  72.     GC bgGC;            /* Graphics context for background.  None
  73.                  * means use widget background. */
  74.     GC fgGC;            /* Graphics context for foreground. */
  75.     StyleValues *sValuePtr;    /* Raw information from which GCs were
  76.                  * derived. */
  77.     Tcl_HashEntry *hPtr;    /* Pointer to entry in styleTable.  Used
  78.                  * to delete entry. */
  79. } Style;
  80.  
  81. /*
  82.  * The following macro determines whether two styles have the same
  83.  * background so that, for example, no beveled border should be drawn
  84.  * between them.
  85.  */
  86.  
  87. #define SAME_BACKGROUND(s1, s2) \
  88.     (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
  89.         && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
  90.         && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
  91.         && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
  92.  
  93. /*
  94.  * The following structure describes one line of the display, which may
  95.  * be either part or all of one line of the text.
  96.  */
  97.  
  98. typedef struct DLine {
  99.     TkTextIndex index;        /* Identifies first character in text
  100.                  * that is displayed on this line. */
  101.     int count;            /* Number of characters accounted for by this
  102.                  * display line, including a trailing space
  103.                  * or newline that isn't actually displayed. */
  104.     int y;            /* Y-position at which line is supposed to
  105.                  * be drawn (topmost pixel of rectangular
  106.                  * area occupied by line). */
  107.     int oldY;            /* Y-position at which line currently
  108.                  * appears on display.  -1 means line isn't
  109.                  * currently visible on display and must be
  110.                  * redrawn.  This is used to move lines by
  111.                  * scrolling rather than re-drawing. */
  112.     int height;            /* Height of line, in pixels. */
  113.     int baseline;        /* Offset of text baseline from y, in
  114.                  * pixels. */
  115.     int spaceAbove;        /* How much extra space was added to the
  116.                  * top of the line because of spacing
  117.                  * options.  This is included in height
  118.                  * and baseline. */
  119.     int spaceBelow;        /* How much extra space was added to the
  120.                  * bottom of the line because of spacing
  121.                  * options.  This is included in height. */
  122.     int length;            /* Total length of line, in pixels. */
  123.     TkTextDispChunk *chunkPtr;    /* Pointer to first chunk in list of all
  124.                  * of those that are displayed on this
  125.                  * line of the screen. */
  126.     struct DLine *nextPtr;    /* Next in list of all display lines for
  127.                  * this window.   The list is sorted in
  128.                  * order from top to bottom.  Note:  the
  129.                  * next DLine doesn't always correspond
  130.                  * to the next line of text:  (a) can have
  131.                  * multiple DLines for one text line, and
  132.                  * (b) can have gaps where DLine's have been
  133.                  * deleted because they're out of date. */
  134.     int flags;            /* Various flag bits:  see below for values. */
  135. } DLine;
  136.  
  137. /*
  138.  * Flag bits for DLine structures:
  139.  *
  140.  * HAS_3D_BORDER -        Non-zero means that at least one of the
  141.  *                chunks in this line has a 3D border, so
  142.  *                it potentially interacts with 3D borders
  143.  *                in neighboring lines (see
  144.  *                DisplayLineBackground).
  145.  * NEW_LAYOUT -            Non-zero means that the line has been
  146.  *                re-layed out since the last time the
  147.  *                display was updated.
  148.  * TOP_LINE -            Non-zero means that this was the top line
  149.  *                in the window the last time that the window
  150.  *                was laid out.  This is important because
  151.  *                a line may be displayed differently if its
  152.  *                at the top or bottom than if it's in the
  153.  *                middle (e.g. beveled edges aren't displayed
  154.  *                for middle lines if the adjacent line has
  155.  *                a similar background).
  156.  * BOTTOM_LINE -        Non-zero means that this was the bottom line
  157.  *                in the window the last time that the window
  158.  *                was laid out.
  159.  */
  160.  
  161. #define HAS_3D_BORDER    1
  162. #define NEW_LAYOUT    2
  163. #define TOP_LINE    4
  164. #define BOTTOM_LINE    8
  165.  
  166. /*
  167.  * Overall display information for a text widget:
  168.  */
  169.  
  170. typedef struct DInfo {
  171.     Tcl_HashTable styleTable;    /* Hash table that maps from StyleValues
  172.                  * to Styles for this widget. */
  173.     DLine *dLinePtr;        /* First in list of all display lines for
  174.                  * this widget, in order from top to bottom. */
  175.     GC copyGC;            /* Graphics context for copying from off-
  176.                  * screen pixmaps onto screen. */
  177.     GC scrollGC;        /* Graphics context for copying from one place
  178.                  * in the window to another (scrolling):
  179.                  * differs from copyGC in that we need to get
  180.                  * GraphicsExpose events. */
  181.     int x;            /* First x-coordinate that may be used for
  182.                  * actually displaying line information.
  183.                  * Leaves space for border, etc. */
  184.     int y;            /* First y-coordinate that may be used for
  185.                  * actually displaying line information.
  186.                  * Leaves space for border, etc. */
  187.     int maxX;            /* First x-coordinate to right of available
  188.                  * space for displaying lines. */
  189.     int maxY;            /* First y-coordinate below available
  190.                  * space for displaying lines. */
  191.     int topOfEof;        /* Top-most pixel (lowest y-value) that has
  192.                  * been drawn in the appropriate fashion for
  193.                  * the portion of the window after the last
  194.                  * line of the text.  This field is used to
  195.                  * figure out when to redraw part or all of
  196.                  * the eof field. */
  197.  
  198.     /*
  199.      * Information used for scrolling:
  200.      */
  201.  
  202.     int newCharOffset;        /* Desired x scroll position, measured as the
  203.                  * number of average-size characters off-screen
  204.                  * to the left for a line with no left
  205.                  * margin. */
  206.     int curPixelOffset;        /* Actual x scroll position, measured as the
  207.                  * number of pixels off-screen to the left. */
  208.     int maxLength;        /* Length in pixels of longest line that's
  209.                  * visible in window (length may exceed window
  210.                  * size).  If there's no wrapping, this will
  211.                  * be zero. */
  212.     double xScrollFirst, xScrollLast;
  213.                 /* Most recent values reported to horizontal
  214.                  * scrollbar;  used to eliminate unnecessary
  215.                  * reports. */
  216.     double yScrollFirst, yScrollLast;
  217.                 /* Most recent values reported to vertical
  218.                  * scrollbar;  used to eliminate unnecessary
  219.                  * reports. */
  220.  
  221.     /*
  222.      * The following information is used to implement scanning:
  223.      */
  224.  
  225.     int scanMarkChar;        /* Character that was at the left edge of
  226.                  * the window when the scan started. */
  227.     int scanMarkX;        /* X-position of mouse at time scan started. */
  228.     int scanTotalScroll;    /* Total scrolling (in screen lines) that has
  229.                  * occurred since scanMarkY was set. */
  230.     int scanMarkY;        /* Y-position of mouse at time scan started. */
  231.  
  232.     /*
  233.      * Miscellaneous information:
  234.      */
  235.  
  236.     int dLinesInvalidated;    /* This value is set to 1 whenever something
  237.                  * happens that invalidates information in
  238.                  * DLine structures;  if a redisplay
  239.                  * is in progress, it will see this and
  240.                  * abort the redisplay.  This is needed
  241.                  * because, for example, an embedded window
  242.                  * could change its size when it is first
  243.                  * displayed, invalidating the DLine that
  244.                  * is currently being displayed.  If redisplay
  245.                  * continues, it will use freed memory and
  246.                  * could dump core. */
  247.     int flags;            /* Various flag values:  see below for
  248.                  * definitions. */
  249. } DInfo;
  250.  
  251. /*
  252.  * In TkTextDispChunk structures for character segments, the clientData
  253.  * field points to one of the following structures:
  254.  */
  255.  
  256. typedef struct CharInfo {
  257.     int numChars;        /* Number of characters to display. */
  258.     char chars[4];        /* Characters to display.  Actual size
  259.                  * will be numChars, not 4.  THIS MUST BE
  260.                  * THE LAST FIELD IN THE STRUCTURE. */
  261. } CharInfo;
  262.  
  263. /*
  264.  * Flag values for DInfo structures:
  265.  *
  266.  * DINFO_OUT_OF_DATE:        Non-zero means that the DLine structures
  267.  *                for this window are partially or completely
  268.  *                out of date and need to be recomputed.
  269.  * REDRAW_PENDING:        Means that a when-idle handler has been
  270.  *                scheduled to update the display.
  271.  * REDRAW_BORDERS:        Means window border or pad area has
  272.  *                potentially been damaged and must be redrawn.
  273.  * REPICK_NEEDED:        1 means that the widget has been modified
  274.  *                in a way that could change the current
  275.  *                character (a different character might be
  276.  *                under the mouse cursor now).  Need to
  277.  *                recompute the current character before
  278.  *                the next redisplay.
  279.  */
  280.  
  281. #define DINFO_OUT_OF_DATE    1
  282. #define REDRAW_PENDING        2
  283. #define REDRAW_BORDERS        4
  284. #define REPICK_NEEDED        8
  285.  
  286. /*
  287.  * The following counters keep statistics about redisplay that can be
  288.  * checked to see how clever this code is at reducing redisplays.
  289.  */
  290.  
  291. static int numRedisplays;    /* Number of calls to DisplayText. */
  292. static int linesRedrawn;    /* Number of calls to DisplayDLine. */
  293. static int numCopies;        /* Number of calls to XCopyArea to copy part
  294.                  * of the screen. */
  295. static int damagedCopies;    /* Number of times that XCopyAreas didn't
  296.                  * completely work because some of the source
  297.                  * information was damaged. */
  298.  
  299. /*
  300.  * Forward declarations for procedures defined later in this file:
  301.  */
  302.  
  303. static void        AdjustForTab _ANSI_ARGS_((TkText *textPtr,
  304.                 TkTextTabArray *tabArrayPtr, int index,
  305.                 TkTextDispChunk *chunkPtr));
  306. static void        CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  307.                 int index, int y, int lineHeight, int baseline,
  308.                 int *xPtr, int *yPtr, int *widthPtr,
  309.                 int *heightPtr));
  310. static void        CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  311.                 int x, int y, int height, int baseline,
  312.                 Display *display, Drawable dst, int screenY));
  313. static int        CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  314.                 int x));
  315. static void        CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  316.                 TkTextDispChunk *chunkPtr));
  317. static void        DisplayDLine _ANSI_ARGS_((TkText *textPtr,
  318.                 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  319. static void        DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
  320.                 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  321. static void        DisplayText _ANSI_ARGS_((ClientData clientData));
  322. static DLine *        FindDLine _ANSI_ARGS_((DLine *dlPtr,
  323.                 TkTextIndex *indexPtr));
  324. static void        FreeDLines _ANSI_ARGS_((TkText *textPtr,
  325.                 DLine *firstPtr, DLine *lastPtr, int unlink));
  326. static void        FreeStyle _ANSI_ARGS_((TkText *textPtr,
  327.                 Style *stylePtr));
  328. static Style *        GetStyle _ANSI_ARGS_((TkText *textPtr,
  329.                 TkTextIndex *indexPtr));
  330. static void        GetXView _ANSI_ARGS_((Tcl_Interp *interp,
  331.                 TkText *textPtr, int report));
  332. static void        GetYView _ANSI_ARGS_((Tcl_Interp *interp,
  333.                 TkText *textPtr, int report));
  334. static DLine *        LayoutDLine _ANSI_ARGS_((TkText *textPtr,
  335.                 TkTextIndex *indexPtr));
  336. static void        MeasureUp _ANSI_ARGS_((TkText *textPtr,
  337.                 TkTextIndex *srcPtr, int distance,
  338.                 TkTextIndex *dstPtr));
  339. static void        UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
  340. static void        ScrollByLines _ANSI_ARGS_((TkText *textPtr,
  341.                 int offset));
  342. static int        SizeOfTab _ANSI_ARGS_((TkText *textPtr,
  343.                 TkTextTabArray *tabArrayPtr, int index, int x,
  344.                 int maxX));
  345.  
  346. /*
  347.  *----------------------------------------------------------------------
  348.  *
  349.  * TkTextCreateDInfo --
  350.  *
  351.  *    This procedure is called when a new text widget is created.
  352.  *    Its job is to set up display-related information for the widget.
  353.  *
  354.  * Results:
  355.  *    None.
  356.  *
  357.  * Side effects:
  358.  *    A DInfo data structure is allocated and initialized and attached
  359.  *    to textPtr.
  360.  *
  361.  *----------------------------------------------------------------------
  362.  */
  363.  
  364. void
  365. TkTextCreateDInfo(textPtr)
  366.     TkText *textPtr;        /* Overall information for text widget. */
  367. {
  368.     register DInfo *dInfoPtr;
  369.     XGCValues gcValues;
  370.  
  371.     dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
  372.     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
  373.     dInfoPtr->dLinePtr = NULL;
  374.     dInfoPtr->copyGC = None;
  375.     gcValues.graphics_exposures = True;
  376.     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
  377.         &gcValues);
  378.     dInfoPtr->topOfEof = 0;
  379.     dInfoPtr->newCharOffset = 0;
  380.     dInfoPtr->curPixelOffset = 0;
  381.     dInfoPtr->maxLength = 0;
  382.     dInfoPtr->xScrollFirst = -1;
  383.     dInfoPtr->xScrollLast = -1;
  384.     dInfoPtr->yScrollFirst = -1;
  385.     dInfoPtr->yScrollLast = -1;
  386.     dInfoPtr->scanMarkChar = 0;
  387.     dInfoPtr->scanMarkX = 0;
  388.     dInfoPtr->scanTotalScroll = 0;
  389.     dInfoPtr->scanMarkY = 0;
  390.     dInfoPtr->dLinesInvalidated = 0;
  391.     dInfoPtr->flags = DINFO_OUT_OF_DATE;
  392.     textPtr->dInfoPtr = dInfoPtr;
  393. }
  394.  
  395. /*
  396.  *----------------------------------------------------------------------
  397.  *
  398.  * TkTextFreeDInfo --
  399.  *
  400.  *    This procedure is called to free up all of the private display
  401.  *    information kept by this file for a text widget.
  402.  *
  403.  * Results:
  404.  *    None.
  405.  *
  406.  * Side effects:
  407.  *    Lots of resources get freed.
  408.  *
  409.  *----------------------------------------------------------------------
  410.  */
  411.  
  412. void
  413. TkTextFreeDInfo(textPtr)
  414.     TkText *textPtr;        /* Overall information for text widget. */
  415. {
  416.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  417.  
  418.     /*
  419.      * Be careful to free up styleTable *after* freeing up all the
  420.      * DLines, so that the hash table is still intact to free up the
  421.      * style-related information from the lines.  Once the lines are
  422.      * all free then styleTable will be empty.
  423.      */
  424.  
  425.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  426.     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
  427.     if (dInfoPtr->copyGC != None) {
  428.     Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  429.     }
  430.     Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
  431.     if (dInfoPtr->flags & REDRAW_PENDING) {
  432.     Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
  433.     }
  434.     ckfree((char *) dInfoPtr);
  435. }
  436.  
  437. /*
  438.  *----------------------------------------------------------------------
  439.  *
  440.  * GetStyle --
  441.  *
  442.  *    This procedure creates all the information needed to display
  443.  *    text at a particular location.
  444.  *
  445.  * Results:
  446.  *    The return value is a pointer to a Style structure that
  447.  *    corresponds to *sValuePtr.
  448.  *
  449.  * Side effects:
  450.  *    A new entry may be created in the style table for the widget.
  451.  *
  452.  *----------------------------------------------------------------------
  453.  */
  454.  
  455. static Style *
  456. GetStyle(textPtr, indexPtr)
  457.     TkText *textPtr;        /* Overall information about text widget. */
  458.     TkTextIndex *indexPtr;    /* The character in the text for which
  459.                  * display information is wanted. */
  460. {
  461.     TkTextTag **tagPtrs;
  462.     register TkTextTag *tagPtr;
  463.     StyleValues styleValues;
  464.     Style *stylePtr;
  465.     Tcl_HashEntry *hPtr;
  466.     int numTags, new, i;
  467.     XGCValues gcValues;
  468.     unsigned long mask;
  469.  
  470.     /*
  471.      * The variables below keep track of the highest-priority specification
  472.      * that has occurred for each of the various fields of the StyleValues.
  473.      */
  474.  
  475.     int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
  476.     int fgPrio, fontPrio, fgStipplePrio;
  477.     int underlinePrio, justifyPrio, offsetPrio;
  478.     int lMargin1Prio, lMargin2Prio, rMarginPrio;
  479.     int spacing1Prio, spacing2Prio, spacing3Prio;
  480.     int overstrikePrio, tabPrio, wrapPrio;
  481.  
  482.     /*
  483.      * Find out what tags are present for the character, then compute
  484.      * a StyleValues structure corresponding to those tags (scan
  485.      * through all of the tags, saving information for the highest-
  486.      * priority tag).
  487.      */
  488.  
  489.     tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
  490.     borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
  491.     fgPrio = fontPrio = fgStipplePrio = -1;
  492.     underlinePrio = justifyPrio = offsetPrio = -1;
  493.     lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
  494.     spacing1Prio = spacing2Prio = spacing3Prio = -1;
  495.     overstrikePrio = tabPrio = wrapPrio = -1;
  496.     memset((VOID *) &styleValues, 0, sizeof(StyleValues));
  497.     styleValues.relief = TK_RELIEF_FLAT;
  498.     styleValues.fgColor = textPtr->fgColor;
  499.     styleValues.fontPtr = textPtr->fontPtr;
  500.     styleValues.justify = TK_JUSTIFY_LEFT;
  501.     styleValues.spacing1 = textPtr->spacing1;
  502.     styleValues.spacing2 = textPtr->spacing2;
  503.     styleValues.spacing3 = textPtr->spacing3;
  504.     styleValues.tabArrayPtr = textPtr->tabArrayPtr;
  505.     styleValues.wrapMode = textPtr->wrapMode;
  506.     for (i = 0 ; i < numTags; i++) {
  507.     tagPtr = tagPtrs[i];
  508.     if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
  509.         styleValues.border = tagPtr->border;
  510.         borderPrio = tagPtr->priority;
  511.     }
  512.     if ((tagPtr->bdString != NULL)
  513.         && (tagPtr->priority > borderWidthPrio)) {
  514.         styleValues.borderWidth = tagPtr->borderWidth;
  515.         borderWidthPrio = tagPtr->priority;
  516.     }
  517.     if ((tagPtr->reliefString != NULL)
  518.         && (tagPtr->priority > reliefPrio)) {
  519.         if (styleValues.border == NULL) {
  520.         styleValues.border = textPtr->border;
  521.         }
  522.         styleValues.relief = tagPtr->relief;
  523.         reliefPrio = tagPtr->priority;
  524.     }
  525.     if ((tagPtr->bgStipple != None)
  526.         && (tagPtr->priority > bgStipplePrio)) {
  527.         styleValues.bgStipple = tagPtr->bgStipple;
  528.         bgStipplePrio = tagPtr->priority;
  529.     }
  530.     if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
  531.         styleValues.fgColor = tagPtr->fgColor;
  532.         fgPrio = tagPtr->priority;
  533.     }
  534.     if ((tagPtr->fontPtr != None) && (tagPtr->priority > fontPrio)) {
  535.         styleValues.fontPtr = tagPtr->fontPtr;
  536.         fontPrio = tagPtr->priority;
  537.     }
  538.     if ((tagPtr->fgStipple != None)
  539.         && (tagPtr->priority > fgStipplePrio)) {
  540.         styleValues.fgStipple = tagPtr->fgStipple;
  541.         fgStipplePrio = tagPtr->priority;
  542.     }
  543.     if ((tagPtr->justifyString != NULL)
  544.         && (tagPtr->priority > justifyPrio)) {
  545.         styleValues.justify = tagPtr->justify;
  546.         justifyPrio = tagPtr->priority;
  547.     }
  548.     if ((tagPtr->lMargin1String != NULL)
  549.         && (tagPtr->priority > lMargin1Prio)) {
  550.         styleValues.lMargin1 = tagPtr->lMargin1;
  551.         lMargin1Prio = tagPtr->priority;
  552.     }
  553.     if ((tagPtr->lMargin2String != NULL)
  554.         && (tagPtr->priority > lMargin2Prio)) {
  555.         styleValues.lMargin2 = tagPtr->lMargin2;
  556.         lMargin2Prio = tagPtr->priority;
  557.     }
  558.     if ((tagPtr->offsetString != NULL)
  559.         && (tagPtr->priority > offsetPrio)) {
  560.         styleValues.offset = tagPtr->offset;
  561.         offsetPrio = tagPtr->priority;
  562.     }
  563.     if ((tagPtr->overstrikeString != NULL)
  564.         && (tagPtr->priority > overstrikePrio)) {
  565.         styleValues.overstrike = tagPtr->overstrike;
  566.         overstrikePrio = tagPtr->priority;
  567.     }
  568.     if ((tagPtr->rMarginString != NULL)
  569.         && (tagPtr->priority > rMarginPrio)) {
  570.         styleValues.rMargin = tagPtr->rMargin;
  571.         rMarginPrio = tagPtr->priority;
  572.     }
  573.     if ((tagPtr->spacing1String != NULL)
  574.         && (tagPtr->priority > spacing1Prio)) {
  575.         styleValues.spacing1 = tagPtr->spacing1;
  576.         spacing1Prio = tagPtr->priority;
  577.     }
  578.     if ((tagPtr->spacing2String != NULL)
  579.         && (tagPtr->priority > spacing2Prio)) {
  580.         styleValues.spacing2 = tagPtr->spacing2;
  581.         spacing2Prio = tagPtr->priority;
  582.     }
  583.     if ((tagPtr->spacing3String != NULL)
  584.         && (tagPtr->priority > spacing3Prio)) {
  585.         styleValues.spacing3 = tagPtr->spacing3;
  586.         spacing3Prio = tagPtr->priority;
  587.     }
  588.     if ((tagPtr->tabString != NULL)
  589.         && (tagPtr->priority > tabPrio)) {
  590.         styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
  591.         tabPrio = tagPtr->priority;
  592.     }
  593.     if ((tagPtr->underlineString != NULL)
  594.         && (tagPtr->priority > underlinePrio)) {
  595.         styleValues.underline = tagPtr->underline;
  596.         underlinePrio = tagPtr->priority;
  597.     }
  598.     if ((tagPtr->wrapMode != NULL)
  599.         && (tagPtr->priority > wrapPrio)) {
  600.         styleValues.wrapMode = tagPtr->wrapMode;
  601.         wrapPrio = tagPtr->priority;
  602.     }
  603.     }
  604.     if (tagPtrs != NULL) {
  605.     ckfree((char *) tagPtrs);
  606.     }
  607.  
  608.     /*
  609.      * Use an existing style if there's one around that matches.
  610.      */
  611.  
  612.     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
  613.         (char *) &styleValues, &new);
  614.     if (!new) {
  615.     stylePtr = (Style *) Tcl_GetHashValue(hPtr);
  616.     stylePtr->refCount++;
  617.     return stylePtr;
  618.     }
  619.  
  620.     /*
  621.      * No existing style matched.  Make a new one.
  622.      */
  623.  
  624.     stylePtr = (Style *) ckalloc(sizeof(Style));
  625.     stylePtr->refCount = 1;
  626.     if (styleValues.border != NULL) {
  627.     gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
  628.     mask = GCForeground;
  629.     if (styleValues.bgStipple != None) {
  630.         gcValues.stipple = styleValues.bgStipple;
  631.         gcValues.fill_style = FillStippled;
  632.         mask |= GCStipple|GCFillStyle;
  633.     }
  634.     stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  635.     } else {
  636.     stylePtr->bgGC = None;
  637.     }
  638.     mask = GCForeground|GCFont;
  639.     gcValues.foreground = styleValues.fgColor->pixel;
  640.     gcValues.font = styleValues.fontPtr->fid;
  641.     if (styleValues.fgStipple != None) {
  642.     gcValues.stipple = styleValues.fgStipple;
  643.     gcValues.fill_style = FillStippled;
  644.     mask |= GCStipple|GCFillStyle;
  645.     }
  646.     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  647.     stylePtr->sValuePtr = (StyleValues *)
  648.         Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
  649.     stylePtr->hPtr = hPtr;
  650.     Tcl_SetHashValue(hPtr, stylePtr);
  651.     return stylePtr;
  652. }
  653.  
  654. /*
  655.  *----------------------------------------------------------------------
  656.  *
  657.  * FreeStyle --
  658.  *
  659.  *    This procedure is called when a Style structure is no longer
  660.  *    needed.  It decrements the reference count and frees up the
  661.  *    space for the style structure if the reference count is 0.
  662.  *
  663.  * Results:
  664.  *    None.
  665.  *
  666.  * Side effects:
  667.  *    The storage and other resources associated with the style
  668.  *    are freed up if no-one's still using it.
  669.  *
  670.  *----------------------------------------------------------------------
  671.  */
  672.  
  673. static void
  674. FreeStyle(textPtr, stylePtr)
  675.     TkText *textPtr;        /* Information about overall widget. */
  676.     register Style *stylePtr;    /* Information about style to be freed. */
  677.  
  678. {
  679.     stylePtr->refCount--;
  680.     if (stylePtr->refCount == 0) {
  681.     if (stylePtr->bgGC != None) {
  682.         Tk_FreeGC(textPtr->display, stylePtr->bgGC);
  683.     }
  684.     Tk_FreeGC(textPtr->display, stylePtr->fgGC);
  685.     Tcl_DeleteHashEntry(stylePtr->hPtr);
  686.     ckfree((char *) stylePtr);
  687.     }
  688. }
  689.  
  690. /*
  691.  *----------------------------------------------------------------------
  692.  *
  693.  * LayoutDLine --
  694.  *
  695.  *    This procedure generates a single DLine structure for a display
  696.  *    line whose leftmost character is given by indexPtr.
  697.  *    
  698.  * Results:
  699.  *    The return value is a pointer to a DLine structure desribing the
  700.  *    display line.  All fields are filled in and correct except for
  701.  *    y and nextPtr.
  702.  *
  703.  * Side effects:
  704.  *    Storage is allocated for the new DLine.
  705.  *
  706.  *----------------------------------------------------------------------
  707.  */
  708.  
  709. static DLine *
  710. LayoutDLine(textPtr, indexPtr)
  711.     TkText *textPtr;        /* Overall information about text widget. */
  712.     TkTextIndex *indexPtr;    /* Beginning of display line.  May not
  713.                  * necessarily point to a character segment. */
  714. {
  715.     register DLine *dlPtr;        /* New display line. */
  716.     TkTextSegment *segPtr;        /* Current segment in text. */
  717.     TkTextDispChunk *lastChunkPtr;    /* Last chunk allocated so far
  718.                      * for line. */
  719.     TkTextDispChunk *chunkPtr;        /* Current chunk. */
  720.     TkTextIndex curIndex;
  721.     TkTextDispChunk *breakChunkPtr;    /* Chunk containing best word break
  722.                      * point, if any. */
  723.     TkTextIndex breakIndex;        /* Index of first character in
  724.                      * breakChunkPtr. */
  725.     int breakCharOffset;        /* Character within breakChunkPtr just
  726.                      * to right of best break point. */
  727.     int noCharsYet;            /* Non-zero means that no characters
  728.                      * have been placed on the line yet. */
  729.     int justify;            /* How to justify line: taken from
  730.                      * style for first character in line. */
  731.     int jIndent;            /* Additional indentation (beyond
  732.                      * margins) due to justification. */
  733.     int rMargin;            /* Right margin width for line. */
  734.     Tk_Uid wrapMode;            /* Wrap mode to use for this line. */
  735.     int x = 0, maxX = 0;        /* Initializations needed only to
  736.                      * stop compiler warnings. */
  737.     int wholeLine;            /* Non-zero means this display line
  738.                      * runs to the end of the text line. */
  739.     int tabIndex;            /* Index of the current tab stop. */
  740.     int gotTab;                /* Non-zero means the current chunk
  741.                      * contains a tab. */
  742.     TkTextDispChunk *tabChunkPtr;    /* Pointer to the chunk containing
  743.                      * the previous tab stop. */
  744.     int maxChars;            /* Maximum number of characters to
  745.                      * include in this chunk. */
  746.     TkTextTabArray *tabArrayPtr;    /* Tab stops for line;  taken from
  747.                      * style for first character on line. */
  748.     int tabSize;            /* Number of pixels consumed by current
  749.                      * tab stop. */
  750.     int offset, ascent, descent, code;
  751.     StyleValues *sValuePtr;
  752.  
  753.     /*
  754.      * Create and initialize a new DLine structure.
  755.      */
  756.  
  757.     dlPtr = (DLine *) ckalloc(sizeof(DLine));
  758.     dlPtr->index = *indexPtr;
  759.     dlPtr->count = 0;
  760.     dlPtr->y = 0;
  761.     dlPtr->oldY = -1;
  762.     dlPtr->height = 0;
  763.     dlPtr->baseline = 0;
  764.     dlPtr->chunkPtr = NULL;
  765.     dlPtr->nextPtr = NULL;
  766.     dlPtr->flags = NEW_LAYOUT;
  767.  
  768.     /*
  769.      * Each iteration of the loop below creates one TkTextDispChunk for
  770.      * the new display line.  The line will always have at least one
  771.      * chunk (for the newline character at the end, if there's nothing
  772.      * else available).
  773.      */
  774.  
  775.     curIndex = *indexPtr;
  776.     lastChunkPtr = NULL;
  777.     chunkPtr = NULL;
  778.     noCharsYet = 1;
  779.     breakChunkPtr = NULL;
  780.     breakCharOffset = 0;
  781.     justify = TK_JUSTIFY_LEFT;
  782.     tabIndex = -1;
  783.     tabChunkPtr = NULL;
  784.     tabArrayPtr = NULL;
  785.     rMargin = 0;
  786.     wrapMode = tkTextCharUid;
  787.     tabSize = 0;
  788.  
  789.     /*
  790.      * Find the first segment to consider for the line.  Can't call
  791.      * TkTextIndexToSeg for this because it won't return a segment
  792.      * with zero size (such as the insertion cursor's mark).
  793.      */
  794.  
  795.     for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
  796.         (offset > 0) && (offset >= segPtr->size);
  797.         offset -= segPtr->size, segPtr = segPtr->nextPtr) {
  798.     /* Empty loop body. */
  799.     }
  800.  
  801.     while (segPtr != NULL) {
  802.     if (segPtr->typePtr->layoutProc == NULL) {
  803.         segPtr = segPtr->nextPtr;
  804.         offset = 0;
  805.         continue;
  806.     }
  807.     if (chunkPtr == NULL) {
  808.         chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
  809.         chunkPtr->nextPtr = NULL;
  810.     }
  811.     chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
  812.  
  813.     /*
  814.      * Save style information such as justification and indentation,
  815.      * up until the first character is encountered, then retain that
  816.      * information for the rest of the line.
  817.      */
  818.  
  819.     if (noCharsYet) {
  820.         tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
  821.         justify = chunkPtr->stylePtr->sValuePtr->justify;
  822.         rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
  823.         wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
  824.         x = ((curIndex.charIndex == 0)
  825.             ? chunkPtr->stylePtr->sValuePtr->lMargin1
  826.             : chunkPtr->stylePtr->sValuePtr->lMargin2);
  827.         if (wrapMode == tkTextNoneUid) {
  828.         maxX = INT_MAX;
  829.         } else {
  830.         maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
  831.             - rMargin;
  832.         if (maxX < x) {
  833.             maxX = x;
  834.         }
  835.         }
  836.     }
  837.  
  838.     /*
  839.      * See if there is a tab in the current chunk; if so, only
  840.      * layout characters up to (and including) the tab.
  841.      */
  842.  
  843.     gotTab = 0;
  844.     maxChars = segPtr->size - offset;
  845.     if (justify == TK_JUSTIFY_LEFT) {
  846.         if (segPtr->typePtr == &tkTextCharType) {
  847.         char *p;
  848.  
  849.         for (p = segPtr->body.chars  + offset; *p != 0; p++) {
  850.             if (*p == '\t') {
  851.             maxChars = (p + 1 - segPtr->body.chars) - offset;
  852.             gotTab = 1;
  853.             break;
  854.             }
  855.         }
  856.         }
  857.     }
  858.  
  859.     chunkPtr->x = x;
  860.     code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
  861.         offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
  862.         chunkPtr);
  863.     if (code <= 0) {
  864.         FreeStyle(textPtr, chunkPtr->stylePtr);
  865.         if (code < 0) {
  866.         /*
  867.          * This segment doesn't wish to display itself (e.g. most
  868.          * marks).
  869.          */
  870.  
  871.         segPtr = segPtr->nextPtr;
  872.         offset = 0;
  873.         continue;
  874.         }
  875.  
  876.         /*
  877.          * No characters from this segment fit in the window: this
  878.          * means we're at the end of the display line.
  879.          */
  880.  
  881.         if (chunkPtr != NULL) {
  882.         ckfree((char *) chunkPtr);
  883.         }
  884.         break;
  885.     }
  886.     if (chunkPtr->numChars > 0) {
  887.         noCharsYet = 0;
  888.     }
  889.     if (lastChunkPtr == NULL) {
  890.         dlPtr->chunkPtr = chunkPtr;
  891.     } else {
  892.         lastChunkPtr->nextPtr = chunkPtr;
  893.     }
  894.     lastChunkPtr = chunkPtr;
  895.     x += chunkPtr->width;
  896.     if (chunkPtr->breakIndex > 0) {
  897.         breakCharOffset = chunkPtr->breakIndex;
  898.         breakIndex = curIndex;
  899.         breakChunkPtr = chunkPtr;
  900.     }
  901.     if (chunkPtr->numChars != maxChars) {
  902.         break;
  903.     }
  904.  
  905.     /*
  906.      * If we're at a new tab, adjust the layout for all the chunks
  907.      * pertaining to the previous tab.  Also adjust the amount of
  908.      * space left in the line to account for space that will be eaten
  909.      * up by the tab.
  910.      */
  911.  
  912.     if (gotTab) {
  913.         if (tabIndex >= 0) {
  914.         AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  915.         x = chunkPtr->x + chunkPtr->width;
  916.         }
  917.         tabIndex++;
  918.         tabChunkPtr = chunkPtr;
  919.         tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
  920.         if (tabSize >= (maxX - x)) {
  921.         break;
  922.         }
  923.     }
  924.     curIndex.charIndex += chunkPtr->numChars;
  925.     offset += chunkPtr->numChars;
  926.     if (offset >= segPtr->size) {
  927.         offset = 0;
  928.         segPtr = segPtr->nextPtr;
  929.     }
  930.     chunkPtr = NULL;
  931.     }
  932.     if (noCharsYet) {
  933.     panic("LayoutDLine couldn't place any characters on a line");
  934.     }
  935.     wholeLine = (segPtr == NULL);
  936.  
  937.     /*
  938.      * We're at the end of the display line.  Throw away everything
  939.      * after the most recent word break, if there is one;  this may
  940.      * potentially require the last chunk to be layed out again.
  941.      */
  942.  
  943.     if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
  944.         || (breakCharOffset != lastChunkPtr->numChars))) {
  945.     while (1) {
  946.         chunkPtr = breakChunkPtr->nextPtr;
  947.         if (chunkPtr == NULL) {
  948.         break;
  949.         }
  950.         FreeStyle(textPtr, chunkPtr->stylePtr);
  951.         breakChunkPtr->nextPtr = chunkPtr->nextPtr;
  952.         (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  953.         ckfree((char *) chunkPtr);
  954.     }
  955.     if (breakCharOffset != breakChunkPtr->numChars) {
  956.         (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
  957.         segPtr = TkTextIndexToSeg(&breakIndex, &offset);
  958.         (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
  959.             segPtr, offset, maxX, breakCharOffset, 0, 
  960.             wrapMode, breakChunkPtr);
  961.     }
  962.     lastChunkPtr = breakChunkPtr;
  963.     wholeLine = 0;
  964.     }
  965.  
  966.     /*
  967.      * Make tab adjustments for the last tab stop, if there is one.
  968.      */
  969.  
  970.     if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
  971.     AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  972.     }
  973.  
  974.     /*
  975.      * Make one more pass over the line to recompute various things
  976.      * like its height, length, and total number of characters.  Also
  977.      * modify the x-locations of chunks to reflect justification.
  978.      * If we're not wrapping, I'm not sure what is the best way to
  979.      * handle left and center justification:  should the total length,
  980.      * for purposes of justification, be (a) the window width, (b)
  981.      * the length of the longest line in the window, or (c) the length
  982.      * of the longest line in the text?  (c) isn't available, (b) seems
  983.      * weird, since it can change with vertical scrolling, so (a) is
  984.      * what is implemented below.
  985.      */
  986.  
  987.     if (wrapMode == tkTextNoneUid) {
  988.     maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
  989.     }
  990.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  991.     if (justify == TK_JUSTIFY_LEFT) {
  992.     jIndent = 0;
  993.     } else if (justify == TK_JUSTIFY_RIGHT) {
  994.     jIndent = maxX - dlPtr->length;
  995.     } else {
  996.     jIndent = (maxX - dlPtr->length)/2;
  997.     }
  998.     ascent = descent = 0;
  999.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1000.         chunkPtr = chunkPtr->nextPtr) {
  1001.     chunkPtr->x += jIndent;
  1002.     dlPtr->count += chunkPtr->numChars;
  1003.     if (chunkPtr->minAscent > ascent) {
  1004.         ascent = chunkPtr->minAscent;
  1005.     }
  1006.     if (chunkPtr->minDescent > descent) {
  1007.         descent = chunkPtr->minDescent;
  1008.     }
  1009.     if (chunkPtr->minHeight > dlPtr->height) {
  1010.         dlPtr->height = chunkPtr->minHeight;
  1011.     }
  1012.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1013.     if ((sValuePtr->borderWidth > 0)
  1014.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1015.         dlPtr->flags |= HAS_3D_BORDER;
  1016.     }
  1017.     }
  1018.     if (dlPtr->height < (ascent + descent)) {
  1019.     dlPtr->height = ascent + descent;
  1020.     dlPtr->baseline = ascent;
  1021.     } else {
  1022.     dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
  1023.     }
  1024.     sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
  1025.     if (dlPtr->index.charIndex == 0) {
  1026.     dlPtr->spaceAbove = sValuePtr->spacing1;
  1027.     } else {
  1028.     dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
  1029.     }
  1030.     if (wholeLine) {
  1031.     dlPtr->spaceBelow = sValuePtr->spacing3;
  1032.     } else {
  1033.     dlPtr->spaceBelow = sValuePtr->spacing2/2;
  1034.     }
  1035.     dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
  1036.     dlPtr->baseline += dlPtr->spaceAbove;
  1037.  
  1038.     /*
  1039.      * Recompute line length:  may have changed because of justification.
  1040.      */
  1041.  
  1042.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  1043.     return dlPtr;
  1044. }
  1045.  
  1046. /*
  1047.  *----------------------------------------------------------------------
  1048.  *
  1049.  * UpdateDisplayInfo --
  1050.  *
  1051.  *    This procedure is invoked to recompute some or all of the
  1052.  *    DLine structures for a text widget.  At the time it is called
  1053.  *    the DLine structures still left in the widget are guaranteed
  1054.  *    to be correct except that (a) the y-coordinates aren't
  1055.  *    necessarily correct, (b) there may be missing structures
  1056.  *    (the DLine structures get removed as soon as they are potentially
  1057.  *    out-of-date), and (c) DLine structures that don't start at the
  1058.  *    beginning of a line may be incorrect if previous information in
  1059.  *    the same line changed size in a way that moved a line boundary
  1060.  *    (DLines for any info that changed will have been deleted, but
  1061.  *    not DLines for unchanged info in the same text line).
  1062.  *
  1063.  * Results:
  1064.  *    None.
  1065.  *
  1066.  * Side effects:
  1067.  *    Upon return, the DLine information for textPtr correctly reflects
  1068.  *    the positions where characters will be displayed.  However, this
  1069.  *    procedure doesn't actually bring the display up-to-date.
  1070.  *
  1071.  *----------------------------------------------------------------------
  1072.  */
  1073.  
  1074. static void
  1075. UpdateDisplayInfo(textPtr)
  1076.     TkText *textPtr;            /* Text widget to update. */
  1077. {
  1078.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  1079.     register DLine *dlPtr, *prevPtr;
  1080.     TkTextIndex index;
  1081.     TkTextLine *lastLinePtr;
  1082.     int y, maxY, pixelOffset, maxOffset;
  1083.  
  1084.     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
  1085.     return;
  1086.     }
  1087.     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
  1088.  
  1089.     /*
  1090.      * Delete any DLines that are now above the top of the window.
  1091.      */
  1092.  
  1093.     index = textPtr->topIndex;
  1094.     dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
  1095.     if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
  1096.     FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
  1097.     }
  1098.  
  1099.     /*
  1100.      *--------------------------------------------------------------
  1101.      * Scan through the contents of the window from top to bottom,
  1102.      * recomputing information for lines that are missing.
  1103.      *--------------------------------------------------------------
  1104.      */
  1105.  
  1106.     lastLinePtr = TkBTreeFindLine(textPtr->tree,
  1107.         TkBTreeNumLines(textPtr->tree));
  1108.     dlPtr = dInfoPtr->dLinePtr;
  1109.     prevPtr = NULL;
  1110.     y = dInfoPtr->y;
  1111.     maxY = dInfoPtr->maxY;
  1112.     while (1) {
  1113.     register DLine *newPtr;
  1114.  
  1115.     if (index.linePtr == lastLinePtr) {
  1116.         break;
  1117.     }
  1118.  
  1119.     /*
  1120.      * There are three possibilities right now:
  1121.      * (a) the next DLine (dlPtr) corresponds exactly to the next
  1122.      *     information we want to display: just use it as-is.
  1123.      * (b) the next DLine corresponds to a different line, or to
  1124.      *     a segment that will be coming later in the same line:
  1125.      *     leave this DLine alone in the hopes that we'll be able
  1126.      *     to use it later, then create a new DLine in front of
  1127.      *     it.
  1128.      * (c) the next DLine corresponds to a segment in the line we
  1129.      *     want, but it's a segment that has already been processed
  1130.      *     or will never be processed.  Delete the DLine and try
  1131.      *     again.
  1132.      *
  1133.      * One other twist on all this.  It's possible for 3D borders
  1134.      * to interact between lines (see DisplayLineBackground) so if
  1135.      * a line is relayed out and has styles with 3D borders, its
  1136.      * neighbors have to be redrawn if they have 3D borders too,
  1137.      * since the interactions could have changed (the neighbors
  1138.      * don't have to be relayed out, just redrawn).
  1139.      */
  1140.  
  1141.     if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
  1142.         /*
  1143.          * Case (b) -- must make new DLine.
  1144.          */
  1145.  
  1146.         makeNewDLine:
  1147.         if (tkTextDebug) {
  1148.         char string[TK_POS_CHARS];
  1149.  
  1150.         /*
  1151.          * Debugging is enabled, so keep a log of all the lines
  1152.          * that were re-layed out.  The test suite uses this
  1153.          * information.
  1154.          */
  1155.  
  1156.         TkTextPrintIndex(&index, string);
  1157.         Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
  1158.             string,
  1159.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1160.         }
  1161.         newPtr = LayoutDLine(textPtr, &index);
  1162.         if (prevPtr == NULL) {
  1163.         dInfoPtr->dLinePtr = newPtr;
  1164.         } else {
  1165.         prevPtr->nextPtr = newPtr;
  1166.         if (prevPtr->flags & HAS_3D_BORDER) {
  1167.             prevPtr->oldY = -1;
  1168.         }
  1169.         }
  1170.         newPtr->nextPtr = dlPtr;
  1171.         dlPtr = newPtr;
  1172.     } else {
  1173.         /*
  1174.          * DlPtr refers to the line we want.  Next check the
  1175.          * index within the line.
  1176.          */
  1177.  
  1178.         if (index.charIndex == dlPtr->index.charIndex) {
  1179.         /*
  1180.          * Case (a) -- can use existing display line as-is.
  1181.          */
  1182.  
  1183.         if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
  1184.             && (prevPtr->flags & (NEW_LAYOUT))) {
  1185.             dlPtr->oldY = -1;
  1186.         }
  1187.         goto lineOK;
  1188.         }
  1189.         if (index.charIndex < dlPtr->index.charIndex) {
  1190.         goto makeNewDLine;
  1191.         }
  1192.  
  1193.         /*
  1194.          * Case (c) -- dlPtr is useless.  Discard it and start
  1195.          * again with the next display line.
  1196.          */
  1197.  
  1198.         newPtr = dlPtr->nextPtr;
  1199.         FreeDLines(textPtr, dlPtr, newPtr, 0);
  1200.         dlPtr = newPtr;
  1201.         continue;
  1202.     }
  1203.  
  1204.     /*
  1205.      * Advance to the start of the next line.
  1206.      */
  1207.  
  1208.     lineOK:
  1209.     dlPtr->y = y;
  1210.     y += dlPtr->height;
  1211.     TkTextIndexForwChars(&index, dlPtr->count, &index);
  1212.     prevPtr = dlPtr;
  1213.     dlPtr = dlPtr->nextPtr;
  1214.  
  1215.     /*
  1216.      * If we switched text lines, delete any DLines left for the
  1217.      * old text line.
  1218.      */
  1219.  
  1220.     if (index.linePtr != prevPtr->index.linePtr) {
  1221.         register DLine *nextPtr;
  1222.  
  1223.         nextPtr = dlPtr;
  1224.         while ((nextPtr != NULL)
  1225.             && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
  1226.         nextPtr = nextPtr->nextPtr;
  1227.         }
  1228.         if (nextPtr != dlPtr) {
  1229.         FreeDLines(textPtr, dlPtr, nextPtr, 0);
  1230.         prevPtr->nextPtr = nextPtr;
  1231.         dlPtr = nextPtr;
  1232.         }
  1233.     }
  1234.  
  1235.     /*
  1236.      * It's important to have the following check here rather than in
  1237.      * the while statement for the loop, so that there's always at least
  1238.      * one DLine generated, regardless of how small the window is.  This
  1239.      * keeps a lot of other code from breaking.
  1240.      */
  1241.  
  1242.     if (y >= maxY) {
  1243.         break;
  1244.     }
  1245.     }
  1246.  
  1247.     /*
  1248.      * Delete any DLine structures that don't fit on the screen.
  1249.      */
  1250.  
  1251.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
  1252.  
  1253.     /*
  1254.      *--------------------------------------------------------------
  1255.      * If there is extra space at the bottom of the window (because
  1256.      * we've hit the end of the text), then bring in more lines at
  1257.      * the top of the window, if there are any, to fill in the view.
  1258.      *--------------------------------------------------------------
  1259.      */
  1260.  
  1261.     if (y < maxY) {
  1262.     int lineNum, spaceLeft, charsToCount;
  1263.     DLine *lowestPtr;
  1264.  
  1265.     /*
  1266.      * Layout an entire text line (potentially > 1 display line),
  1267.      * then link in as many display lines as fit without moving
  1268.      * the bottom line out of the window.  Repeat this until
  1269.      * all the extra space has been used up or we've reached the
  1270.      * beginning of the text.
  1271.      */
  1272.  
  1273.     spaceLeft = maxY - y;
  1274.     lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
  1275.     charsToCount = dInfoPtr->dLinePtr->index.charIndex;
  1276.     if (charsToCount == 0) {
  1277.         charsToCount = INT_MAX;
  1278.         lineNum--;
  1279.     }
  1280.     for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
  1281.         index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
  1282.         index.charIndex = 0;
  1283.         lowestPtr = NULL;
  1284.         do {
  1285.         dlPtr = LayoutDLine(textPtr, &index);
  1286.         dlPtr->nextPtr = lowestPtr;
  1287.         lowestPtr = dlPtr;
  1288.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  1289.         charsToCount -= dlPtr->count;
  1290.         } while ((charsToCount > 0)
  1291.             && (index.linePtr == lowestPtr->index.linePtr));
  1292.  
  1293.         /*
  1294.          * Scan through the display lines from the bottom one up to
  1295.          * the top one.
  1296.          */
  1297.  
  1298.         while (lowestPtr != NULL) {
  1299.         dlPtr = lowestPtr;
  1300.         spaceLeft -= dlPtr->height;
  1301.         if (spaceLeft < 0) {
  1302.             break;
  1303.         }
  1304.         lowestPtr = dlPtr->nextPtr;
  1305.         dlPtr->nextPtr = dInfoPtr->dLinePtr;
  1306.         dInfoPtr->dLinePtr = dlPtr;
  1307.         if (tkTextDebug) {
  1308.             char string[TK_POS_CHARS];
  1309.  
  1310.             TkTextPrintIndex(&dlPtr->index, string);
  1311.             Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
  1312.                 (char *) NULL, string,
  1313.                 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1314.         }
  1315.         }
  1316.         FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  1317.         charsToCount = INT_MAX;
  1318.     }
  1319.  
  1320.     /*
  1321.      * Now we're all done except that the y-coordinates in all the
  1322.      * DLines are wrong and the top index for the text is wrong.
  1323.      * Update them.
  1324.      */
  1325.  
  1326.     textPtr->topIndex = dInfoPtr->dLinePtr->index;
  1327.     y = dInfoPtr->y;
  1328.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1329.         dlPtr = dlPtr->nextPtr) {
  1330.         if (y > dInfoPtr->maxY) {
  1331.         panic("Added too many new lines in UpdateDisplayInfo");
  1332.         }
  1333.         dlPtr->y = y;
  1334.         y += dlPtr->height; 
  1335.     }
  1336.     }
  1337.  
  1338.     /*
  1339.      *--------------------------------------------------------------
  1340.      * If the old top or bottom line has scrolled elsewhere on the
  1341.      * screen, we may not be able to re-use its old contents by
  1342.      * copying bits (e.g., a beveled edge that was drawn when it was
  1343.      * at the top or bottom won't be drawn when the line is in the
  1344.      * middle and its neighbor has a matching background).  Similarly,
  1345.      * if the new top or bottom line came from somewhere else on the
  1346.      * screen, we may not be able to copy the old bits.
  1347.      *--------------------------------------------------------------
  1348.      */
  1349.  
  1350.     dlPtr = dInfoPtr->dLinePtr;
  1351.     if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
  1352.     dlPtr->oldY = -1;
  1353.     }
  1354.     while (1) {
  1355.     if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
  1356.         && (dlPtr->flags & HAS_3D_BORDER)) {
  1357.         dlPtr->oldY = -1;
  1358.     }
  1359.     if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
  1360.         && (dlPtr->flags & HAS_3D_BORDER)) {
  1361.         dlPtr->oldY = -1;
  1362.     }
  1363.     if (dlPtr->nextPtr == NULL) {
  1364.         if ((dlPtr->flags & HAS_3D_BORDER)
  1365.             && !(dlPtr->flags & BOTTOM_LINE)) {
  1366.         dlPtr->oldY = -1;
  1367.         }
  1368.         dlPtr->flags &= ~TOP_LINE;
  1369.         dlPtr->flags |= BOTTOM_LINE;
  1370.         break;
  1371.     }
  1372.     dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
  1373.     dlPtr = dlPtr->nextPtr;
  1374.     }
  1375.     dInfoPtr->dLinePtr->flags |= TOP_LINE;
  1376.  
  1377.     /*
  1378.      * Arrange for scrollbars to be updated.
  1379.      */
  1380.  
  1381.     textPtr->flags |= UPDATE_SCROLLBARS;
  1382.  
  1383.     /*
  1384.      *--------------------------------------------------------------
  1385.      * Deal with horizontal scrolling:
  1386.      * 1. If there's empty space to the right of the longest line,
  1387.      *    shift the screen to the right to fill in the empty space.
  1388.      * 2. If the desired horizontal scroll position has changed,
  1389.      *    force a full redisplay of all the lines in the widget.
  1390.      * 3. If the wrap mode isn't "none" then re-scroll to the base
  1391.      *    position.
  1392.      *--------------------------------------------------------------
  1393.      */
  1394.  
  1395.     dInfoPtr->maxLength = 0;
  1396.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1397.         dlPtr = dlPtr->nextPtr) {
  1398.     if (dlPtr->length > dInfoPtr->maxLength) {
  1399.         dInfoPtr->maxLength = dlPtr->length;
  1400.     }
  1401.     }
  1402.     maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
  1403.         + textPtr->charWidth - 1)/textPtr->charWidth;
  1404.     if (dInfoPtr->newCharOffset > maxOffset) {
  1405.     dInfoPtr->newCharOffset = maxOffset;
  1406.     }
  1407.     if (dInfoPtr->newCharOffset < 0) {
  1408.     dInfoPtr->newCharOffset = 0;
  1409.     }
  1410.     pixelOffset = dInfoPtr->newCharOffset * textPtr->charWidth;
  1411.     if (pixelOffset != dInfoPtr->curPixelOffset) {
  1412.     dInfoPtr->curPixelOffset = pixelOffset;
  1413.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1414.         dlPtr = dlPtr->nextPtr) {
  1415.         dlPtr->oldY = -1;
  1416.     }
  1417.     }
  1418. }
  1419.  
  1420. /*
  1421.  *----------------------------------------------------------------------
  1422.  *
  1423.  * FreeDLines --
  1424.  *
  1425.  *    This procedure is called to free up all of the resources
  1426.  *    associated with one or more DLine structures.
  1427.  *
  1428.  * Results:
  1429.  *    None.
  1430.  *
  1431.  * Side effects:
  1432.  *    Memory gets freed and various other resources are released.
  1433.  *
  1434.  *----------------------------------------------------------------------
  1435.  */
  1436.  
  1437. static void
  1438. FreeDLines(textPtr, firstPtr, lastPtr, unlink)
  1439.     TkText *textPtr;            /* Information about overall text
  1440.                      * widget. */
  1441.     register DLine *firstPtr;        /* Pointer to first DLine to free up. */
  1442.     DLine *lastPtr;            /* Pointer to DLine just after last
  1443.                      * one to free (NULL means everything
  1444.                      * starting with firstPtr). */
  1445.     int unlink;                /* 1 means DLines are currently linked
  1446.                      * into the list rooted at
  1447.                      * textPtr->dInfoPtr->dLinePtr and
  1448.                      * they have to be unlinked.  0 means
  1449.                      * just free without unlinking. */
  1450. {
  1451.     register TkTextDispChunk *chunkPtr, *nextChunkPtr;
  1452.     register DLine *nextDLinePtr;
  1453.  
  1454.     if (unlink) {
  1455.     if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
  1456.         textPtr->dInfoPtr->dLinePtr = lastPtr;
  1457.     } else {
  1458.         register DLine *prevPtr;
  1459.         for (prevPtr = textPtr->dInfoPtr->dLinePtr;
  1460.             prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
  1461.         /* Empty loop body. */
  1462.         }
  1463.         prevPtr->nextPtr = lastPtr;
  1464.     }
  1465.     }
  1466.     while (firstPtr != lastPtr) {
  1467.     nextDLinePtr = firstPtr->nextPtr;
  1468.     for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
  1469.         chunkPtr = nextChunkPtr) {
  1470.         if (chunkPtr->undisplayProc != NULL) {
  1471.         (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  1472.         }
  1473.         FreeStyle(textPtr, chunkPtr->stylePtr);
  1474.         nextChunkPtr = chunkPtr->nextPtr;
  1475.         ckfree((char *) chunkPtr);
  1476.     }
  1477.     ckfree((char *) firstPtr);
  1478.     firstPtr = nextDLinePtr;
  1479.     }
  1480.     textPtr->dInfoPtr->dLinesInvalidated = 1;
  1481. }
  1482.  
  1483. /*
  1484.  *----------------------------------------------------------------------
  1485.  *
  1486.  * DisplayDLine --
  1487.  *
  1488.  *    This procedure is invoked to draw a single line on the
  1489.  *    screen.
  1490.  *
  1491.  * Results:
  1492.  *    None.
  1493.  *
  1494.  * Side effects:
  1495.  *    The line given by dlPtr is drawn at its correct position in
  1496.  *    textPtr's window.  Note that this is one *display* line, not
  1497.  *    one *text* line.
  1498.  *
  1499.  *----------------------------------------------------------------------
  1500.  */
  1501.  
  1502. static void
  1503. DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
  1504.     TkText *textPtr;        /* Text widget in which to draw line. */
  1505.     register DLine *dlPtr;    /* Information about line to draw. */
  1506.     DLine *prevPtr;        /* Line just before one to draw, or NULL
  1507.                  * if dlPtr is the top line. */
  1508.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1509.                  * Caller must make sure it's large enough
  1510.                  * to hold line. */
  1511. {
  1512.     register TkTextDispChunk *chunkPtr;
  1513.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1514.     Display *display;
  1515.     int height, x;
  1516.  
  1517.     /*
  1518.      * First, clear the area of the line to the background color for the
  1519.      * text widget.
  1520.      */
  1521.  
  1522.     display = Tk_Display(textPtr->tkwin);
  1523.     Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
  1524.         Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
  1525.  
  1526.     /*
  1527.      * Next, draw background information for the whole line.
  1528.      */
  1529.  
  1530.     DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
  1531.  
  1532.     /*
  1533.      * Make another pass through all of the chunks to redraw the
  1534.      * insertion cursor, if it is visible on this line.  Must do
  1535.      * it here rather than in the foreground pass below because
  1536.      * otherwise a wide insertion cursor will obscure the character
  1537.      * to its left.
  1538.      */
  1539.  
  1540.     if (textPtr->state == tkNormalUid) {
  1541.     for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1542.         chunkPtr = chunkPtr->nextPtr) {
  1543.         x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1544.         if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1545.         (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
  1546.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1547.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1548.             dlPtr->y + dlPtr->spaceAbove);
  1549.         }
  1550.     }
  1551.     }
  1552.  
  1553.     /*
  1554.      * Make yet another pass through all of the chunks to redraw all of
  1555.      * foreground information.  Note:  we have to call the displayProc
  1556.      * even for chunks that are off-screen.  This is needed, for
  1557.      * example, so that embedded windows can be unmapped in this case.
  1558.      * Conve
  1559.      */
  1560.  
  1561.     for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1562.         chunkPtr = chunkPtr->nextPtr) {
  1563.     if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1564.         /*
  1565.          * Already displayed the insertion cursor above.  Don't
  1566.          * do it again here.
  1567.          */
  1568.  
  1569.         continue;
  1570.     }
  1571.     x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1572.     if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
  1573.         /*
  1574.          * Note:  we have to call the displayProc even for chunks
  1575.          * that are off-screen.  This is needed, for example, so
  1576.          * that embedded windows can be unmapped in this case.
  1577.          * Display the chunk at a coordinate that can be clearly
  1578.          * identified by the displayProc as being off-screen to
  1579.          * the left (the displayProc may not be able to tell if
  1580.          * something is off to the right).
  1581.          */
  1582.  
  1583.         (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
  1584.             dlPtr->spaceAbove,
  1585.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1586.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1587.             dlPtr->y + dlPtr->spaceAbove);
  1588.     } else {
  1589.         (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
  1590.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1591.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1592.             dlPtr->y + dlPtr->spaceAbove);
  1593.     }
  1594.     if (dInfoPtr->dLinesInvalidated) {
  1595.         return;
  1596.     }
  1597.     }
  1598.  
  1599.     /*
  1600.      * Copy the pixmap onto the screen.  If this is the last line on
  1601.      * the screen then copy a piece of the line, so that it doesn't
  1602.      * overflow into the border area.  Another special trick:  copy the
  1603.      * padding area to the left of the line;  this is because the
  1604.      * insertion cursor sometimes overflows onto that area and we want
  1605.      * to get as much of the cursor as possible.
  1606.      */
  1607.  
  1608.     height = dlPtr->height;
  1609.     if ((height + dlPtr->y) > dInfoPtr->maxY) {
  1610.     height = dInfoPtr->maxY - dlPtr->y;
  1611.     }
  1612.     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
  1613.         dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
  1614.         (unsigned) height, dInfoPtr->x, dlPtr->y);
  1615.     linesRedrawn++;
  1616. }
  1617.  
  1618. /*
  1619.  *--------------------------------------------------------------
  1620.  *
  1621.  * DisplayLineBackground --
  1622.  *
  1623.  *    This procedure is called to fill in the background for
  1624.  *    a display line.  It draws 3D borders cleverly so that
  1625.  *    adjacent chunks with the same style (whether on the same
  1626.  *    line or different lines) have a single 3D border around
  1627.  *    the whole region.
  1628.  *
  1629.  * Results:
  1630.  *    There is no return value.  Pixmap is filled in with background
  1631.  *    information for dlPtr.
  1632.  *
  1633.  * Side effects:
  1634.  *    None.
  1635.  *
  1636.  *--------------------------------------------------------------
  1637.  */
  1638.  
  1639. static void
  1640. DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
  1641.     TkText *textPtr;        /* Text widget containing line. */
  1642.     register DLine *dlPtr;    /* Information about line to draw. */
  1643.     DLine *prevPtr;        /* Line just above dlPtr, or NULL if dlPtr
  1644.                  * is the top-most line in the window. */
  1645.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1646.                  * Caller must make sure it's large enough
  1647.                  * to hold line.  Caller must also have
  1648.                  * filled it with the background color for
  1649.                  * the widget. */
  1650. {
  1651.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1652.     TkTextDispChunk *chunkPtr;  /* Pointer to chunk in the current line. */
  1653.     TkTextDispChunk *chunkPtr2;    /* Pointer to chunk in the line above or
  1654.                  * below the current one.  NULL if we're to
  1655.                  * the left of or to the right of the chunks
  1656.                  * in the line. */
  1657.     TkTextDispChunk *nextPtr2;    /* Next chunk after chunkPtr2 (it's not the
  1658.                  * same as chunkPtr2->nextPtr in the case
  1659.                  * where chunkPtr2 is NULL because the line
  1660.                  * is indented). */
  1661.     int leftX;            /* The left edge of the region we're
  1662.                  * currently working on. */
  1663.     int leftXIn;        /* 1 means beveled edge at leftX slopes right
  1664.                  * as it goes down, 0 means it slopes left
  1665.                  * as it goes down. */
  1666.     int rightX;            /* Right edge of chunkPtr. */
  1667.     int rightX2;        /* Right edge of chunkPtr2. */
  1668.     int matchLeft;        /* Does the style of this line match that
  1669.                  * of its neighbor just to the left of
  1670.                  * the current x coordinate? */
  1671.     int matchRight;        /* Does line's style match its neighbor
  1672.                  * just to the right of the current x-coord? */
  1673.     int minX, maxX, xOffset;
  1674.     StyleValues *sValuePtr;
  1675.     Display *display;
  1676.  
  1677.     /*
  1678.      * Pass 1: scan through dlPtr from left to right.  For each range of
  1679.      * chunks with the same style, draw the main background for the style
  1680.      * plus the vertical parts of the 3D borders (the left and right
  1681.      * edges).
  1682.      */
  1683.  
  1684.     display = Tk_Display(textPtr->tkwin);
  1685.     minX = dInfoPtr->curPixelOffset;
  1686.     xOffset = dInfoPtr->x - minX;
  1687.     maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
  1688.     chunkPtr = dlPtr->chunkPtr;
  1689.     leftX = chunkPtr->x;
  1690.     for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
  1691.     if ((chunkPtr->nextPtr != NULL)
  1692.         && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
  1693.         chunkPtr->stylePtr)) {
  1694.         continue;
  1695.     }
  1696.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1697.     rightX = chunkPtr->x + chunkPtr->width;
  1698.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1699.         rightX = maxX;
  1700.     }
  1701.     if (chunkPtr->stylePtr->bgGC != None) {
  1702.         XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
  1703.             leftX + xOffset, 0, (unsigned int) (rightX - leftX),
  1704.             (unsigned int) dlPtr->height);
  1705.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1706.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1707.             leftX + xOffset, 0, sValuePtr->borderWidth,
  1708.             dlPtr->height, 1, sValuePtr->relief);
  1709.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1710.             rightX - sValuePtr->borderWidth + xOffset,
  1711.             0, sValuePtr->borderWidth, dlPtr->height, 0,
  1712.             sValuePtr->relief);
  1713.         }
  1714.     }
  1715.     leftX = rightX;
  1716.     }
  1717.  
  1718.     /*
  1719.      * Pass 2: draw the horizontal bevels along the top of the line.  To
  1720.      * do this, scan through dlPtr from left to right while simultaneously
  1721.      * scanning through the line just above dlPtr.  ChunkPtr2 and nextPtr2
  1722.      * refer to two adjacent chunks in the line above.
  1723.      */
  1724.  
  1725.     chunkPtr = dlPtr->chunkPtr;
  1726.     leftX = chunkPtr->x;
  1727.     leftXIn = 1;
  1728.     rightX = chunkPtr->x + chunkPtr->width;
  1729.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1730.     rightX = maxX;
  1731.     }
  1732.     chunkPtr2 = NULL;
  1733.     if (prevPtr != NULL) {
  1734.     /*
  1735.      * Find the chunk in the previous line that covers leftX.
  1736.      */
  1737.  
  1738.     nextPtr2 = prevPtr->chunkPtr;
  1739.     rightX2 = nextPtr2->x;
  1740.     while (rightX2 <= leftX) {
  1741.         chunkPtr2 = nextPtr2;
  1742.         if (chunkPtr2 == NULL) {
  1743.         break;
  1744.         }
  1745.         nextPtr2 = chunkPtr2->nextPtr;
  1746.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1747.         if (nextPtr2 == NULL) {
  1748.         rightX2 = INT_MAX;
  1749.         }
  1750.     }
  1751.     } else {
  1752.     nextPtr2 = NULL;
  1753.     rightX2 = INT_MAX;
  1754.     }
  1755.  
  1756.     while (leftX < maxX) {
  1757.     matchLeft = (chunkPtr2 != NULL)
  1758.         && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1759.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1760.     if (rightX <= rightX2) {
  1761.         /*
  1762.          * The chunk in our line is about to end.  If its style
  1763.          * changes then draw the bevel for the current style.
  1764.          */
  1765.  
  1766.         if ((chunkPtr->nextPtr == NULL)
  1767.             || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1768.             chunkPtr->nextPtr->stylePtr)) {
  1769.         if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1770.             Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1771.                 sValuePtr->border, leftX + xOffset, 0,
  1772.                 rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1773.                 1, 1, sValuePtr->relief);
  1774.         }
  1775.         leftX = rightX;
  1776.         leftXIn = 1;
  1777.  
  1778.         /*
  1779.          * If the chunk in the line above is also ending at
  1780.          * the same point then advance to the next chunk in
  1781.          * that line.
  1782.          */
  1783.  
  1784.         if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1785.             goto nextChunk2;
  1786.         }
  1787.         }
  1788.         chunkPtr = chunkPtr->nextPtr;
  1789.         if (chunkPtr == NULL) {
  1790.         break;
  1791.         }
  1792.         rightX = chunkPtr->x + chunkPtr->width;
  1793.         if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1794.         rightX = maxX;
  1795.         }
  1796.         continue;
  1797.     }
  1798.  
  1799.     /*
  1800.      * The chunk in the line above is ending at an x-position where
  1801.      * there is no change in the style of the current line.  If the
  1802.      * style above matches the current line on one side of the change
  1803.      * but not on the other, we have to draw an L-shaped piece of
  1804.      * bevel.
  1805.      */
  1806.  
  1807.     matchRight = (nextPtr2 != NULL)
  1808.         && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1809.     if (matchLeft && !matchRight) {
  1810.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1811.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1812.             rightX2 - sValuePtr->borderWidth + xOffset, 0,
  1813.             sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1814.             sValuePtr->relief);
  1815.         }
  1816.         leftX = rightX2 - sValuePtr->borderWidth;
  1817.         leftXIn = 0;
  1818.     } else if (!matchLeft && matchRight
  1819.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1820.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1821.             rightX2 + xOffset, 0, sValuePtr->borderWidth,
  1822.             sValuePtr->borderWidth, 1, sValuePtr->relief);
  1823.         Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1824.             leftX + xOffset, 0, rightX2 + sValuePtr->borderWidth -leftX,
  1825.             sValuePtr->borderWidth, leftXIn, 0, 1,
  1826.             sValuePtr->relief);
  1827.     }
  1828.  
  1829.     nextChunk2:
  1830.     chunkPtr2 = nextPtr2;
  1831.     if (chunkPtr2 == NULL) {
  1832.         rightX2 = INT_MAX;
  1833.     } else {
  1834.         nextPtr2 = chunkPtr2->nextPtr;
  1835.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1836.         if (nextPtr2 == NULL) {
  1837.         rightX2 = INT_MAX;
  1838.         }
  1839.     }
  1840.     }
  1841.     /*
  1842.      * Pass 3: draw the horizontal bevels along the bottom of the line.
  1843.      * This uses the same approach as pass 2.
  1844.      */
  1845.  
  1846.     chunkPtr = dlPtr->chunkPtr;
  1847.     leftX = chunkPtr->x;
  1848.     leftXIn = 0;
  1849.     rightX = chunkPtr->x + chunkPtr->width;
  1850.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1851.     rightX = maxX;
  1852.     }
  1853.     chunkPtr2 = NULL;
  1854.     if (dlPtr->nextPtr != NULL) {
  1855.     /*
  1856.      * Find the chunk in the previous line that covers leftX.
  1857.      */
  1858.  
  1859.     nextPtr2 = dlPtr->nextPtr->chunkPtr;
  1860.     rightX2 = nextPtr2->x;
  1861.     while (rightX2 <= leftX) {
  1862.         chunkPtr2 = nextPtr2;
  1863.         if (chunkPtr2 == NULL) {
  1864.         break;
  1865.         }
  1866.         nextPtr2 = chunkPtr2->nextPtr;
  1867.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1868.         if (nextPtr2 == NULL) {
  1869.         rightX2 = INT_MAX;
  1870.         }
  1871.     }
  1872.     } else {
  1873.     nextPtr2 = NULL;
  1874.     rightX2 = INT_MAX;
  1875.     }
  1876.  
  1877.     while (leftX < maxX) {
  1878.     matchLeft = (chunkPtr2 != NULL)
  1879.         && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1880.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1881.     if (rightX <= rightX2) {
  1882.         if ((chunkPtr->nextPtr == NULL)
  1883.             || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1884.             chunkPtr->nextPtr->stylePtr)) {
  1885.         if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1886.             Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1887.                 sValuePtr->border, leftX + xOffset,
  1888.                 dlPtr->height - sValuePtr->borderWidth,
  1889.                 rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1890.                 0, 0, sValuePtr->relief);
  1891.         }
  1892.         leftX = rightX;
  1893.         leftXIn = 0;
  1894.         if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1895.             goto nextChunk2b;
  1896.         }
  1897.         }
  1898.         chunkPtr = chunkPtr->nextPtr;
  1899.         if (chunkPtr == NULL) {
  1900.         break;
  1901.         }
  1902.         rightX = chunkPtr->x + chunkPtr->width;
  1903.         if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1904.         rightX = maxX;
  1905.         }
  1906.         continue;
  1907.     }
  1908.  
  1909.     matchRight = (nextPtr2 != NULL)
  1910.         && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1911.     if (matchLeft && !matchRight) {
  1912.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1913.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1914.             rightX2 - sValuePtr->borderWidth + xOffset,
  1915.             dlPtr->height - sValuePtr->borderWidth,
  1916.             sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1917.             sValuePtr->relief);
  1918.         }
  1919.         leftX = rightX2 - sValuePtr->borderWidth;
  1920.         leftXIn = 1;
  1921.     } else if (!matchLeft && matchRight
  1922.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1923.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1924.             rightX2 + xOffset, dlPtr->height - sValuePtr->borderWidth,
  1925.             sValuePtr->borderWidth, sValuePtr->borderWidth,
  1926.             1, sValuePtr->relief);
  1927.         Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1928.             leftX + xOffset, dlPtr->height - sValuePtr->borderWidth,
  1929.             rightX2 + sValuePtr->borderWidth - leftX,
  1930.             sValuePtr->borderWidth, leftXIn, 1, 0, sValuePtr->relief);
  1931.     }
  1932.  
  1933.     nextChunk2b:
  1934.     chunkPtr2 = nextPtr2;
  1935.     if (chunkPtr2 == NULL) {
  1936.         rightX2 = INT_MAX;
  1937.     } else {
  1938.         nextPtr2 = chunkPtr2->nextPtr;
  1939.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1940.         if (nextPtr2 == NULL) {
  1941.         rightX2 = INT_MAX;
  1942.         }
  1943.     }
  1944.     }
  1945. }
  1946.  
  1947. /*
  1948.  *----------------------------------------------------------------------
  1949.  *
  1950.  * DisplayText --
  1951.  *
  1952.  *    This procedure is invoked as a when-idle handler to update the
  1953.  *    display.  It only redisplays the parts of the text widget that
  1954.  *    are out of date.
  1955.  *
  1956.  * Results:
  1957.  *    None.
  1958.  *
  1959.  * Side effects:
  1960.  *    Information is redrawn on the screen.
  1961.  *
  1962.  *----------------------------------------------------------------------
  1963.  */
  1964.  
  1965. static void
  1966. DisplayText(clientData)
  1967.     ClientData clientData;    /* Information about widget. */
  1968. {
  1969.     register TkText *textPtr = (TkText *) clientData;
  1970.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1971.     Tk_Window tkwin;
  1972.     register DLine *dlPtr;
  1973.     DLine *prevPtr;
  1974.     Pixmap pixmap;
  1975.     int maxHeight, borders;
  1976.     int bottomY = 0;        /* Initialization needed only to stop
  1977.                  * compiler warnings. */
  1978.  
  1979.     if (textPtr->tkwin == NULL) {
  1980.     /*
  1981.      * The widget has been deleted.  Don't do anything.
  1982.      */
  1983.  
  1984.     return;
  1985.     }
  1986.  
  1987.     if (tkTextDebug) {
  1988.     Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
  1989.         "", TCL_GLOBAL_ONLY);
  1990.     }
  1991.  
  1992.     if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
  1993.         || (dInfoPtr->maxY <= dInfoPtr->y)) {
  1994.     UpdateDisplayInfo(textPtr);
  1995.     dInfoPtr->flags &= ~REDRAW_PENDING;
  1996.     goto doScrollbars;
  1997.     }
  1998.     numRedisplays++;
  1999.     if (tkTextDebug) {
  2000.     Tcl_SetVar2(textPtr->interp, "tk_textRedraw", (char *) NULL,
  2001.         "", TCL_GLOBAL_ONLY);
  2002.     }
  2003.  
  2004.     /*
  2005.      * Choose a new current item if that is needed (this could cause
  2006.      * event handlers to be invoked, hence the preserve/release calls
  2007.      * and the loop, since the handlers could conceivably necessitate
  2008.      * yet another current item calculation).  The tkwin check is because
  2009.      * the whole window could go away in the Tk_Release call.
  2010.      */
  2011.  
  2012.     while (dInfoPtr->flags & REPICK_NEEDED) {
  2013.     Tk_Preserve((ClientData) textPtr);
  2014.     dInfoPtr->flags &= ~REPICK_NEEDED;
  2015.     TkTextPickCurrent(textPtr, &textPtr->pickEvent);
  2016.     tkwin = textPtr->tkwin;
  2017.     Tk_Release((ClientData) textPtr);
  2018.     if (tkwin == NULL) {
  2019.         return;
  2020.     }
  2021.     }
  2022.  
  2023.     /*
  2024.      * First recompute what's supposed to be displayed.
  2025.      */
  2026.  
  2027.     UpdateDisplayInfo(textPtr);
  2028.     dInfoPtr->dLinesInvalidated = 0;
  2029.  
  2030.     /*
  2031.      * See if it's possible to bring some parts of the screen up-to-date
  2032.      * by scrolling (copying from other parts of the screen).
  2033.      */
  2034.  
  2035.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  2036.     register DLine *dlPtr2;
  2037.     int offset, height, y;
  2038.  
  2039.     if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
  2040.         || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
  2041.         continue;
  2042.     }
  2043.  
  2044.     /*
  2045.      * This line is already drawn somewhere in the window so it only
  2046.      * needs to be copied to its new location.  See if there's a group
  2047.      * of lines that can all be copied together.
  2048.      */
  2049.  
  2050.     offset = dlPtr->y - dlPtr->oldY;
  2051.     height = dlPtr->height;
  2052.     y = dlPtr->y;
  2053.     for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
  2054.         dlPtr2 = dlPtr2->nextPtr) {
  2055.         if ((dlPtr2->oldY == -1)
  2056.             || ((dlPtr2->oldY + offset) != dlPtr2->y)
  2057.             || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
  2058.         break;
  2059.         }
  2060.         height += dlPtr2->height;
  2061.     }
  2062.  
  2063.     /*
  2064.      * Copy the information and update the lines to show that they've
  2065.      * been copied.  Reduce the height of the area being copied if
  2066.      * necessary to avoid overwriting the border area.
  2067.      */
  2068.  
  2069.     if ((y + height) > dInfoPtr->maxY) {
  2070.         height = dInfoPtr->maxY -y;
  2071.     }
  2072.     XCopyArea(Tk_Display(textPtr->tkwin), Tk_WindowId(textPtr->tkwin),
  2073.         Tk_WindowId(textPtr->tkwin), dInfoPtr->scrollGC,
  2074.         dInfoPtr->x - textPtr->padX, dlPtr->oldY,
  2075.         (unsigned) (dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX)),
  2076.         (unsigned) height, dInfoPtr->x - textPtr->padX, y);
  2077.     numCopies++;
  2078.     while (1) {
  2079.         dlPtr->oldY = dlPtr->y;
  2080.         if (dlPtr->nextPtr == dlPtr2) {
  2081.         break;
  2082.         }
  2083.         dlPtr = dlPtr->nextPtr;
  2084.     }
  2085.  
  2086.     /*
  2087.      * Scan through the lines following the copied ones to see if
  2088.      * we just overwrote them with the copy operation.  If so, mark
  2089.      * them for redisplay.
  2090.      */
  2091.  
  2092.     for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  2093.         if ((dlPtr2->oldY != -1)
  2094.             && ((dlPtr2->oldY + dlPtr2->height) > y)
  2095.             && (dlPtr2->oldY < (y + height))) {
  2096.         dlPtr2->oldY = -1;
  2097.         }
  2098.     }
  2099.  
  2100.     /*
  2101.      * It's possible that part of the area copied above was obscured.
  2102.      * To handle this situation, read expose-related events generated
  2103.      * during the XCopyArea operation.
  2104.      */
  2105.  
  2106.     while (1) {
  2107.         XEvent event;
  2108.  
  2109.         XWindowEvent(Tk_Display(textPtr->tkwin),
  2110.             Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
  2111.         if (event.type == NoExpose) {
  2112.         break;
  2113.         } else if (event.type == GraphicsExpose) {
  2114.         TkTextRedrawRegion(textPtr, event.xgraphicsexpose.x,
  2115.             event.xgraphicsexpose.y, event.xgraphicsexpose.width,
  2116.             event.xgraphicsexpose.height);
  2117.         if (event.xgraphicsexpose.count == 0) {
  2118.             damagedCopies++;
  2119.             break;
  2120.         }
  2121.         } else if (event.type == Expose) {
  2122.         /*
  2123.          * A tricky situation.  This event must already have been
  2124.          * queued up before the XCopyArea was issued.  If the area
  2125.          * in this event overlaps the area copied, then some of the
  2126.          * bits that were copied were bogus.  The easiest way to
  2127.          * handle this is to issue two redisplays:  one for the
  2128.          * original area and one for the area shifted as if it was
  2129.          * in the copied area.
  2130.          */
  2131.  
  2132.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  2133.             event.xexpose.y, event.xexpose.width,
  2134.             event.xexpose.height);
  2135.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  2136.             event.xexpose.y + offset, event.xexpose.width,
  2137.             event.xexpose.height);
  2138.         } else {
  2139.         panic("DisplayText received unknown exposure event");
  2140.         }
  2141.     }
  2142.     }
  2143.  
  2144.     /*
  2145.      * Clear the REDRAW_PENDING flag here.  This is actually pretty
  2146.      * tricky.  We want to wait until *after* doing the scrolling,
  2147.      * since that could generate more areas to redraw and don't
  2148.      * want to reschedule a redisplay for them.  On the other hand,
  2149.      * we can't wait until after all the redisplaying, because the
  2150.      * act of redisplaying could actually generate more redisplays
  2151.      * (e.g. in the case of a nested window with event bindings triggered
  2152.      * by redisplay).
  2153.      */
  2154.  
  2155.     dInfoPtr->flags &= ~REDRAW_PENDING;
  2156.  
  2157.     /*
  2158.      * Redraw the borders if that's needed.
  2159.      */
  2160.  
  2161.     if (dInfoPtr->flags & REDRAW_BORDERS) {
  2162.     if (tkTextDebug) {
  2163.         Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2164.             (char *) NULL, "borders",
  2165.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2166.     }
  2167.     Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2168.         textPtr->border, textPtr->highlightWidth,
  2169.         textPtr->highlightWidth,
  2170.         Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2171.         Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2172.         textPtr->borderWidth, textPtr->relief);
  2173.     if (textPtr->highlightWidth != 0) {
  2174.         GC gc;
  2175.     
  2176.         if (textPtr->flags & GOT_FOCUS) {
  2177.         gc = Tk_GCForColor(textPtr->highlightColorPtr,
  2178.             Tk_WindowId(textPtr->tkwin));
  2179.         } else {
  2180.         gc = Tk_GCForColor(textPtr->highlightBgColorPtr,
  2181.             Tk_WindowId(textPtr->tkwin));
  2182.         }
  2183.         Tk_DrawFocusHighlight(textPtr->tkwin, gc, textPtr->highlightWidth,
  2184.             Tk_WindowId(textPtr->tkwin));
  2185.     }
  2186.     borders = textPtr->borderWidth + textPtr->highlightWidth;
  2187.     if (textPtr->padY > 0) {
  2188.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2189.             textPtr->border, borders, borders,
  2190.             Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
  2191.             0, TK_RELIEF_FLAT);
  2192.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2193.             textPtr->border, borders,
  2194.             Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
  2195.             Tk_Width(textPtr->tkwin) - 2*borders,
  2196.             textPtr->padY, 0, TK_RELIEF_FLAT);
  2197.     }
  2198.     if (textPtr->padX > 0) {
  2199.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2200.             textPtr->border, borders, borders + textPtr->padY,
  2201.             textPtr->padX,
  2202.             Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2203.             0, TK_RELIEF_FLAT);
  2204.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2205.             textPtr->border,
  2206.             Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
  2207.             borders + textPtr->padY, textPtr->padX,
  2208.             Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2209.             0, TK_RELIEF_FLAT);
  2210.     }
  2211.     dInfoPtr->flags &= ~REDRAW_BORDERS;
  2212.     }
  2213.  
  2214.     /*
  2215.      * Now we have to redraw the lines that couldn't be updated by
  2216.      * scrolling.  First, compute the height of the largest line and
  2217.      * allocate an off-screen pixmap to use for double-buffered
  2218.      * displays.
  2219.      */
  2220.  
  2221.     maxHeight = -1;
  2222.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  2223.         dlPtr = dlPtr->nextPtr) {
  2224.     if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
  2225.         maxHeight = dlPtr->height;
  2226.     }
  2227.     bottomY = dlPtr->y + dlPtr->height;
  2228.     }
  2229.     if (maxHeight > dInfoPtr->maxY) {
  2230.     maxHeight = dInfoPtr->maxY;
  2231.     }
  2232.     if (maxHeight > 0) {
  2233.     pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
  2234.         Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
  2235.         maxHeight, Tk_Depth(textPtr->tkwin));
  2236.     for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
  2237.         (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
  2238.         prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
  2239.         if (dlPtr->oldY != dlPtr->y) {
  2240.         if (tkTextDebug) {
  2241.             char string[TK_POS_CHARS];
  2242.             TkTextPrintIndex(&dlPtr->index, string);
  2243.             Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2244.                 (char *) NULL, string,
  2245.                 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2246.         }
  2247.         DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
  2248.         if (dInfoPtr->dLinesInvalidated) {
  2249.             Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2250.             return;
  2251.         }
  2252.         dlPtr->oldY = dlPtr->y;
  2253.         dlPtr->flags &= ~NEW_LAYOUT;
  2254.         }
  2255.     }
  2256.     Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2257.     }
  2258.  
  2259.     /*
  2260.      * See if we need to refresh the part of the window below the
  2261.      * last line of text (if there is any such area).  Refresh the
  2262.      * padding area on the left too, since the insertion cursor might
  2263.      * have been displayed there previously).
  2264.      */
  2265.  
  2266.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  2267.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  2268.     }
  2269.     if (bottomY < dInfoPtr->topOfEof) {
  2270.     if (tkTextDebug) {
  2271.         Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2272.             (char *) NULL, "eof",
  2273.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2274.     }
  2275.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2276.         textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
  2277.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  2278.         dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
  2279.     }
  2280.     dInfoPtr->topOfEof = bottomY;
  2281.  
  2282.     doScrollbars:
  2283.  
  2284.     /*
  2285.      * Update the vertical scrollbar, if there is one.  Note:  it's
  2286.      * important to clear REDRAW_PENDING here, just in case the
  2287.      * scroll procedure does something that requires redisplay.
  2288.      */
  2289.  
  2290.     if (textPtr->flags & UPDATE_SCROLLBARS) {
  2291.     textPtr->flags &= ~UPDATE_SCROLLBARS;
  2292.     if (textPtr->yScrollCmd != NULL) {
  2293.         GetYView(textPtr->interp, textPtr, 1);
  2294.     }
  2295.  
  2296.     /*
  2297.      * Update the horizontal scrollbar, if any.
  2298.      */
  2299.  
  2300.     if (textPtr->xScrollCmd != NULL) {
  2301.         GetXView(textPtr->interp, textPtr, 1);
  2302.     }
  2303.     }
  2304. }
  2305.  
  2306. /*
  2307.  *----------------------------------------------------------------------
  2308.  *
  2309.  * TkTextEventuallyRepick --
  2310.  *
  2311.  *    This procedure is invoked whenever something happens that
  2312.  *    could change the current character or the tags associated
  2313.  *    with it.
  2314.  *
  2315.  * Results:
  2316.  *    None.
  2317.  *
  2318.  * Side effects:
  2319.  *    A repick is scheduled as an idle handler.
  2320.  *
  2321.  *----------------------------------------------------------------------
  2322.  */
  2323.  
  2324.     /* ARGSUSED */
  2325. void
  2326. TkTextEventuallyRepick(textPtr)
  2327.     TkText *textPtr;        /* Widget record for text widget. */
  2328. {
  2329.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2330.  
  2331.     dInfoPtr->flags |= REPICK_NEEDED;
  2332.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2333.     dInfoPtr->flags |= REDRAW_PENDING;
  2334.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2335.     }
  2336. }
  2337.  
  2338. /*
  2339.  *----------------------------------------------------------------------
  2340.  *
  2341.  * TkTextRedrawRegion --
  2342.  *
  2343.  *    This procedure is invoked to schedule a redisplay for a given
  2344.  *    region of a text widget.  The redisplay itself may not occur
  2345.  *    immediately:  it's scheduled as a when-idle handler.
  2346.  *
  2347.  * Results:
  2348.  *    None.
  2349.  *
  2350.  * Side effects:
  2351.  *    Information will eventually be redrawn on the screen.
  2352.  *
  2353.  *----------------------------------------------------------------------
  2354.  */
  2355.  
  2356.     /* ARGSUSED */
  2357. void
  2358. TkTextRedrawRegion(textPtr, x, y, width, height)
  2359.     TkText *textPtr;        /* Widget record for text widget. */
  2360.     int x, y;            /* Coordinates of upper-left corner of area
  2361.                  * to be redrawn, in pixels relative to
  2362.                  * textPtr's window. */
  2363.     int width, height;        /* Width and height of area to be redrawn. */
  2364. {
  2365.     register DLine *dlPtr;
  2366.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2367.     int maxY, inset;
  2368.  
  2369.     /*
  2370.      * Find all lines that overlap the given region and mark them for
  2371.      * redisplay.
  2372.      */
  2373.  
  2374.     maxY = y + height;
  2375.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  2376.         dlPtr = dlPtr->nextPtr) {
  2377.     if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
  2378.         dlPtr->oldY = -1;
  2379.     }
  2380.     }
  2381.     if (dInfoPtr->topOfEof < maxY) {
  2382.     dInfoPtr->topOfEof = maxY;
  2383.     }
  2384.  
  2385.     /*
  2386.      * Schedule the redisplay operation if there isn't one already
  2387.      * scheduled.
  2388.      */
  2389.  
  2390.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2391.     dInfoPtr->flags |= REDRAW_PENDING;
  2392.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2393.     }
  2394.     inset = textPtr->borderWidth + textPtr->highlightWidth;
  2395.     if ((x < inset) || (y < inset)
  2396.         || ((x + width) > (Tk_Width(textPtr->tkwin) - inset))
  2397.         || (maxY > (Tk_Height(textPtr->tkwin) - inset))) {
  2398.     dInfoPtr->flags |= REDRAW_BORDERS;
  2399.     }
  2400. }
  2401.  
  2402. /*
  2403.  *----------------------------------------------------------------------
  2404.  *
  2405.  * TkTextChanged --
  2406.  *
  2407.  *    This procedure is invoked when info in a text widget is about
  2408.  *    to be modified in a way that changes how it is displayed (e.g.
  2409.  *    characters were inserted or deleted, or tag information was
  2410.  *    changed).  This procedure must be called *before* a change is
  2411.  *    made, so that indexes in the display information are still
  2412.  *    valid.
  2413.  *
  2414.  * Results:
  2415.  *    None.
  2416.  *
  2417.  * Side effects:
  2418.  *    The range of character between index1Ptr (inclusive) and
  2419.  *    index2Ptr (exclusive) will be redisplayed at some point in the
  2420.  *    future (the actual redisplay is scheduled as a when-idle handler).
  2421.  *
  2422.  *----------------------------------------------------------------------
  2423.  */
  2424.  
  2425. void
  2426. TkTextChanged(textPtr, index1Ptr, index2Ptr)
  2427.     TkText *textPtr;        /* Widget record for text widget. */
  2428.     TkTextIndex *index1Ptr;    /* Index of first character to redisplay. */
  2429.     TkTextIndex *index2Ptr;    /* Index of character just after last one
  2430.                  * to redisplay. */
  2431. {
  2432.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2433.     DLine *firstPtr, *lastPtr;
  2434.     TkTextIndex rounded;
  2435.  
  2436.     /*
  2437.      * Find the DLines corresponding to index1Ptr and index2Ptr.  There
  2438.      * is one tricky thing here, which is that we have to relayout in
  2439.      * units of whole text lines:  round index1Ptr back to the beginning
  2440.      * of its text line, and include all the display lines after index2,
  2441.      * up to the end of its text line.  This is necessary because the
  2442.      * indices stored in the display lines will no longer be valid.  It's
  2443.      * also needed because any edit could change the way lines wrap.
  2444.      */
  2445.  
  2446.     rounded = *index1Ptr;
  2447.     rounded.charIndex = 0;
  2448.     firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
  2449.     if (firstPtr == NULL) {
  2450.     return;
  2451.     }
  2452.     lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
  2453.     while ((lastPtr != NULL)
  2454.         && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
  2455.     lastPtr = lastPtr->nextPtr;
  2456.     }
  2457.  
  2458.     /*
  2459.      * Schedule both a redisplay and a recomputation of display information.
  2460.      * It's important to do this BEFORE calling FreeDLines.  The reason for
  2461.      * this is subtle and has to do with embedded windows.  The chunk delete
  2462.      * procedure for an embedded window will schedule an idle handler to
  2463.      * unmap the window.  However, we want the idle handler for redisplay
  2464.      * to be called first, so that it can put the embedded window back on
  2465.      * the screen again (if appropriate).  This will prevent the window from
  2466.      * ever being unmapped, and thereby avoid flashing.
  2467.      */
  2468.  
  2469.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2470.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2471.     }
  2472.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  2473.  
  2474.     /*
  2475.      * Delete all the DLines from firstPtr up to but not including lastPtr.
  2476.      */
  2477.  
  2478.     FreeDLines(textPtr, firstPtr, lastPtr, 1);
  2479. }
  2480.  
  2481. /*
  2482.  *----------------------------------------------------------------------
  2483.  *
  2484.  * TkTextRedrawTag --
  2485.  *
  2486.  *    This procedure is invoked to request a redraw of all characters
  2487.  *    in a given range that have a particular tag on or off.  It's
  2488.  *    called, for example, when tag options change.
  2489.  *
  2490.  * Results:
  2491.  *    None.
  2492.  *
  2493.  * Side effects:
  2494.  *    Information on the screen may be redrawn, and the layout of
  2495.  *    the screen may change.
  2496.  *
  2497.  *----------------------------------------------------------------------
  2498.  */
  2499.  
  2500. void
  2501. TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
  2502.     TkText *textPtr;        /* Widget record for text widget. */
  2503.     TkTextIndex *index1Ptr;    /* First character in range to consider
  2504.                  * for redisplay.  NULL means start at
  2505.                  * beginning of text. */
  2506.     TkTextIndex *index2Ptr;    /* Character just after last one to consider
  2507.                  * for redisplay.  NULL means process all
  2508.                  * the characters in the text. */
  2509.     TkTextTag *tagPtr;        /* Information about tag. */
  2510.     int withTag;        /* 1 means redraw characters that have the
  2511.                  * tag, 0 means redraw those without. */
  2512. {
  2513.     register DLine *dlPtr;
  2514.     DLine *endPtr;
  2515.     int tagOn;
  2516.     TkTextSearch search;
  2517.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2518.     TkTextIndex endOfText, *endIndexPtr;
  2519.  
  2520.     /*
  2521.      * Round up the starting position if it's before the first line
  2522.      * visible on the screen (we only care about what's on the screen).
  2523.      */
  2524.  
  2525.     dlPtr = dInfoPtr->dLinePtr;
  2526.     if (dlPtr == NULL) {
  2527.     return;
  2528.     }
  2529.     if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
  2530.     index1Ptr = &dlPtr->index;
  2531.     }
  2532.  
  2533.     /*
  2534.      * Set the stopping position if it wasn't specified.
  2535.      */
  2536.  
  2537.     if (index2Ptr == NULL) {
  2538.     index2Ptr = TkTextMakeIndex(textPtr->tree,
  2539.         TkBTreeNumLines(textPtr->tree), 0, &endOfText);
  2540.     }
  2541.  
  2542.     /* 
  2543.      * Initialize a search through all transitions on the tag, starting
  2544.      * with the first transition where the tag's current state is different
  2545.      * from what it will eventually be.
  2546.      */
  2547.  
  2548.     TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  2549.     tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
  2550.     if (tagOn != withTag) {
  2551.     if (!TkBTreeNextTag(&search)) {
  2552.         return;
  2553.     }
  2554.     }
  2555.  
  2556.     /*
  2557.      * Schedule a redisplay and layout recalculation if they aren't
  2558.      * already pending.  This has to be done before calling FreeDLines,
  2559.      * for the reason given in TkTextChanged.
  2560.      */
  2561.  
  2562.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2563.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2564.     }
  2565.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  2566.  
  2567.     /*
  2568.      * Each loop through the loop below is for one range of characters
  2569.      * where the tag's current state is different than its eventual
  2570.      * state.  At the top of the loop, search contains information about
  2571.      * the first character in the range.
  2572.      */
  2573.  
  2574.     while (1) {
  2575.     /*
  2576.      * Find the first DLine structure in the range.  Note: if the
  2577.      * desired character isn't the first in its text line, then look
  2578.      * for the character just before it instead.  This is needed to
  2579.      * handle the case where the first character of a wrapped
  2580.      * display line just got smaller, so that it now fits on the
  2581.      * line before:  need to relayout the line containing the
  2582.      * previous character.
  2583.      */
  2584.  
  2585.     if (search.curIndex.charIndex == 0) {
  2586.         dlPtr = FindDLine(dlPtr, &search.curIndex);
  2587.     } else {
  2588.         TkTextIndex tmp;
  2589.  
  2590.         tmp = search.curIndex;
  2591.         tmp.charIndex -= 1;
  2592.         dlPtr = FindDLine(dlPtr, &tmp);
  2593.     }
  2594.     if (dlPtr == NULL) {
  2595.         break;
  2596.     }
  2597.  
  2598.     /*
  2599.      * Find the first DLine structure that's past the end of the range.
  2600.      */
  2601.  
  2602.     if (!TkBTreeNextTag(&search)) {
  2603.         endIndexPtr = index2Ptr;
  2604.     } else {
  2605.         endIndexPtr = &search.curIndex;
  2606.     }
  2607.     endPtr = FindDLine(dlPtr, endIndexPtr);
  2608.     if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
  2609.         && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
  2610.         endPtr = endPtr->nextPtr;
  2611.     }
  2612.  
  2613.     /*
  2614.      * Delete all of the display lines in the range, so that they'll
  2615.      * be re-layed out and redrawn.
  2616.      */
  2617.  
  2618.     FreeDLines(textPtr, dlPtr, endPtr, 1);
  2619.     dlPtr = endPtr;
  2620.  
  2621.     /*
  2622.      * Find the first text line in the next range.
  2623.      */
  2624.  
  2625.     if (!TkBTreeNextTag(&search)) {
  2626.         break;
  2627.     }
  2628.     }
  2629. }
  2630.  
  2631. /*
  2632.  *----------------------------------------------------------------------
  2633.  *
  2634.  * TkTextRelayoutWindow --
  2635.  *
  2636.  *    This procedure is called when something has happened that
  2637.  *    invalidates the whole layout of characters on the screen, such
  2638.  *    as a change in a configuration option for the overall text
  2639.  *    widget or a change in the window size.  It causes all display
  2640.  *    information to be recomputed and the window to be redrawn.
  2641.  *
  2642.  * Results:
  2643.  *    None.
  2644.  *
  2645.  * Side effects:
  2646.  *    All the display information will be recomputed for the window
  2647.  *    and the window will be redrawn.
  2648.  *
  2649.  *----------------------------------------------------------------------
  2650.  */
  2651.  
  2652. void
  2653. TkTextRelayoutWindow(textPtr)
  2654.     TkText *textPtr;        /* Widget record for text widget. */
  2655. {
  2656.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2657.     GC new;
  2658.     XGCValues gcValues;
  2659.  
  2660.     /*
  2661.      * Schedule the window redisplay.  See TkTextChanged for the
  2662.      * reason why this has to be done before any calls to FreeDLines.
  2663.      */
  2664.  
  2665.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2666.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2667.     }
  2668.     dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
  2669.         |REPICK_NEEDED;
  2670.  
  2671.     /*
  2672.      * (Re-)create the graphics context for drawing the traversal
  2673.      * highlight.
  2674.      */
  2675.  
  2676.     gcValues.graphics_exposures = False;
  2677.     new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
  2678.     if (dInfoPtr->copyGC != None) {
  2679.     Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  2680.     }
  2681.     dInfoPtr->copyGC = new;
  2682.  
  2683.     /*
  2684.      * Throw away all the current layout information.
  2685.      */
  2686.  
  2687.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  2688.     dInfoPtr->dLinePtr = NULL;
  2689.  
  2690.     /*
  2691.      * Recompute some overall things for the layout.  Even if the
  2692.      * window gets very small, pretend that there's at least one
  2693.      * pixel of drawing space in it.
  2694.      */
  2695.  
  2696.     if (textPtr->highlightWidth < 0) {
  2697.     textPtr->highlightWidth = 0;
  2698.     }
  2699.     dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
  2700.         + textPtr->padX;
  2701.     dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
  2702.         + textPtr->padY;
  2703.     dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
  2704.         - textPtr->borderWidth - textPtr->padX;
  2705.     if (dInfoPtr->maxX <= dInfoPtr->x) {
  2706.     dInfoPtr->maxX = dInfoPtr->x + 1;
  2707.     }
  2708.     dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
  2709.         - textPtr->borderWidth - textPtr->padY;
  2710.     if (dInfoPtr->maxY <= dInfoPtr->y) {
  2711.     dInfoPtr->maxY = dInfoPtr->y + 1;
  2712.     }
  2713.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  2714.  
  2715.     /*
  2716.      * If the upper-left character isn't the first in a line, recompute
  2717.      * it.  This is necessary because a change in the window's size
  2718.      * or options could change the way lines wrap.
  2719.      */
  2720.  
  2721.     if (textPtr->topIndex.charIndex != 0) {
  2722.     MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
  2723.     }
  2724. }
  2725.  
  2726. /*
  2727.  *----------------------------------------------------------------------
  2728.  *
  2729.  * TkTextSetYView --
  2730.  *
  2731.  *    This procedure is called to specify what lines are to be
  2732.  *    displayed in a text widget.
  2733.  *
  2734.  * Results:
  2735.  *    None.
  2736.  *
  2737.  * Side effects:
  2738.  *    The display will (eventually) be updated so that the position
  2739.  *    given by "indexPtr" is visible on the screen at the position
  2740.  *    determined by "pickPlace".
  2741.  *
  2742.  *----------------------------------------------------------------------
  2743.  */
  2744.  
  2745. void
  2746. TkTextSetYView(textPtr, indexPtr, pickPlace)
  2747.     TkText *textPtr;        /* Widget record for text widget. */
  2748.     TkTextIndex *indexPtr;    /* Position that is to appear somewhere
  2749.                  * in the view. */
  2750.     int pickPlace;        /* 0 means topLine must appear at top of
  2751.                  * screen.  1 means we get to pick where it
  2752.                  * appears:  minimize screen motion or else
  2753.                  * display line at center of screen. */
  2754. {
  2755.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2756.     register DLine *dlPtr;
  2757.     int bottomY, close, lineIndex;
  2758.     TkTextIndex tmpIndex, rounded;
  2759.  
  2760.     /*
  2761.      * If the specified position is the extra line at the end of the
  2762.      * text, round it back to the last real line.
  2763.      */
  2764.  
  2765.     lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
  2766.     if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
  2767.     TkTextIndexBackChars(indexPtr, 1, &rounded);
  2768.     indexPtr = &rounded;
  2769.     }
  2770.  
  2771.     if (!pickPlace) {
  2772.     /*
  2773.      * The specified position must go at the top of the screen.
  2774.      * Just leave all the DLine's alone: we may be able to reuse
  2775.      * some of the information that's currently on the screen
  2776.      * without redisplaying it all.
  2777.      */
  2778.  
  2779.     if (indexPtr->charIndex == 0) {
  2780.         textPtr->topIndex = *indexPtr;
  2781.     } else {
  2782.         MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
  2783.     }
  2784.     goto scheduleUpdate;
  2785.     }
  2786.  
  2787.     /*
  2788.      * We have to pick where to display the index.  First, bring
  2789.      * the display information up to date and see if the index will be
  2790.      * completely visible in the current screen configuration.  If so
  2791.      * then there's nothing to do.
  2792.      */
  2793.  
  2794.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  2795.     UpdateDisplayInfo(textPtr);
  2796.     }
  2797.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  2798.     if (dlPtr != NULL) {
  2799.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  2800.         /*
  2801.          * Part of the line hangs off the bottom of the screen;
  2802.          * pretend the whole line is off-screen.
  2803.          */
  2804.  
  2805.         dlPtr = NULL;
  2806.     } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
  2807.         && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
  2808.         return;
  2809.     }
  2810.     }
  2811.  
  2812.     /*
  2813.      * The desired line isn't already on-screen.
  2814.      */
  2815.  
  2816.     bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
  2817.     close = (dInfoPtr->maxY - dInfoPtr->y)/3;
  2818.     if (dlPtr != NULL) {
  2819.     /*
  2820.      * The desired line is above the top of screen.  If it is
  2821.      * "close" to the top of the window then make it the top
  2822.      * line on the screen.
  2823.      */
  2824.  
  2825.     MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
  2826.     if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
  2827.         MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
  2828.         goto scheduleUpdate;
  2829.     }
  2830.     } else {
  2831.     /*
  2832.      * The desired line is below the bottom of the screen.  If it is
  2833.      * "close" to the bottom of the screen then position it at the
  2834.      * bottom of the screen.
  2835.      */
  2836.  
  2837.     MeasureUp(textPtr, indexPtr, close, &tmpIndex);
  2838.     if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
  2839.         bottomY = dInfoPtr->maxY - dInfoPtr->y;
  2840.     }
  2841.     }
  2842.  
  2843.     /*
  2844.      * Our job now is to arrange the display so that indexPtr appears
  2845.      * as low on the screen as possible but with its bottom no lower
  2846.      * than bottomY.  BottomY is the bottom of the window if the
  2847.      * desired line is just below the current screen, otherwise it
  2848.      * is the center of the window.
  2849.      */
  2850.  
  2851.     MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
  2852.  
  2853.     scheduleUpdate:
  2854.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2855.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2856.     }
  2857.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  2858. }
  2859.  
  2860. /*
  2861.  *--------------------------------------------------------------
  2862.  *
  2863.  * MeasureUp --
  2864.  *
  2865.  *    Given one index, find the index of the first character
  2866.  *    on the highest display line that would be displayed no more
  2867.  *    than "distance" pixels above the given index.
  2868.  *
  2869.  * Results:
  2870.  *    *dstPtr is filled in with the index of the first character
  2871.  *    on a display line.  The display line is found by measuring
  2872.  *    up "distance" pixels above the pixel just below an imaginary
  2873.  *    display line that contains srcPtr.  If the display line
  2874.  *    that covers this coordinate actually extends above the 
  2875.  *    coordinate, then return the index of the next lower line
  2876.  *    instead (i.e. the returned index will be completely visible
  2877.  *    at or below the given y-coordinate).
  2878.  *
  2879.  * Side effects:
  2880.  *    None.
  2881.  *
  2882.  *--------------------------------------------------------------
  2883.  */
  2884.  
  2885. static void
  2886. MeasureUp(textPtr, srcPtr, distance, dstPtr)
  2887.     TkText *textPtr;        /* Text widget in which to measure. */
  2888.     TkTextIndex *srcPtr;    /* Index of character from which to start
  2889.                  * measuring. */
  2890.     int distance;        /* Vertical distance in pixels measured
  2891.                  * from the pixel just below the lowest
  2892.                  * one in srcPtr's line. */
  2893.     TkTextIndex *dstPtr;    /* Index to fill in with result. */
  2894. {
  2895.     int lineNum;        /* Number of current line. */
  2896.     int charsToCount;        /* Maximum number of characters to measure
  2897.                  * in current line. */
  2898.     TkTextIndex bestIndex;    /* Best candidate seen so far for result. */
  2899.     TkTextIndex index;
  2900.     DLine *dlPtr, *lowestPtr;
  2901.     int noBestYet;        /* 1 means bestIndex hasn't been set. */
  2902.  
  2903.     noBestYet = 1;
  2904.     charsToCount = srcPtr->charIndex + 1;
  2905.     index.tree = srcPtr->tree;
  2906.     for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
  2907.         lineNum--) {
  2908.     /*
  2909.      * Layout an entire text line (potentially > 1 display line).
  2910.      * For the first line, which contains srcPtr, only layout the
  2911.      * part up through srcPtr (charsToCount is non-infinite to
  2912.      * accomplish this).  Make a list of all the display lines
  2913.      * in backwards order (the lowest DLine on the screen is first
  2914.      * in the list).
  2915.      */
  2916.  
  2917.     index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
  2918.     index.charIndex = 0;
  2919.     lowestPtr = NULL;
  2920.     do {
  2921.         dlPtr = LayoutDLine(textPtr, &index);
  2922.         dlPtr->nextPtr = lowestPtr;
  2923.         lowestPtr = dlPtr;
  2924.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  2925.         charsToCount -= dlPtr->count;
  2926.     } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
  2927.  
  2928.     /*
  2929.      * Scan through the display lines to see if we've covered enough
  2930.      * vertical distance.  If so, save the starting index for the
  2931.      * line at the desired location.
  2932.      */
  2933.  
  2934.     for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  2935.         distance -= dlPtr->height;
  2936.         if (distance < 0) {
  2937.         *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
  2938.         break;
  2939.         }
  2940.         bestIndex = dlPtr->index;
  2941.         noBestYet = 0;
  2942.     }
  2943.  
  2944.     /*
  2945.      * Discard the display lines, then either return or prepare
  2946.      * for the next display line to lay out.
  2947.      */
  2948.  
  2949.     FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  2950.     if (distance < 0) {
  2951.         return;
  2952.     }
  2953.     charsToCount = INT_MAX;        /* Consider all chars. in next line. */
  2954.     }
  2955.  
  2956.     /*
  2957.      * Ran off the beginning of the text.  Return the first character
  2958.      * in the text.
  2959.      */
  2960.  
  2961.     TkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
  2962. }
  2963.  
  2964. /*
  2965.  *--------------------------------------------------------------
  2966.  *
  2967.  * TkTextSeeCmd --
  2968.  *
  2969.  *    This procedure is invoked to process the "see" option for
  2970.  *    the widget command for text widgets. See the user documentation
  2971.  *    for details on what it does.
  2972.  *
  2973.  * Results:
  2974.  *    A standard Tcl result.
  2975.  *
  2976.  * Side effects:
  2977.  *    See the user documentation.
  2978.  *
  2979.  *--------------------------------------------------------------
  2980.  */
  2981.  
  2982. int
  2983. TkTextSeeCmd(textPtr, interp, argc, argv)
  2984.     TkText *textPtr;        /* Information about text widget. */
  2985.     Tcl_Interp *interp;        /* Current interpreter. */
  2986.     int argc;            /* Number of arguments. */
  2987.     char **argv;        /* Argument strings.  Someone else has already
  2988.                  * parsed this command enough to know that
  2989.                  * argv[1] is "see". */
  2990. {
  2991.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2992.     TkTextIndex index;
  2993.     int x, y, width, height, lineWidth, charCount, oneThird, delta;
  2994.     DLine *dlPtr;
  2995.     TkTextDispChunk *chunkPtr;
  2996.  
  2997.     if (argc != 3) {
  2998.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  2999.         argv[0], " see index\"", (char *) NULL);
  3000.     return TCL_ERROR;
  3001.     }
  3002.     if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
  3003.     return TCL_ERROR;
  3004.     }
  3005.  
  3006.     /*
  3007.      * If the specified position is the extra line at the end of the
  3008.      * text, round it back to the last real line.
  3009.      */
  3010.  
  3011.     if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
  3012.     TkTextIndexBackChars(&index, 1, &index);
  3013.     }
  3014.  
  3015.     /*
  3016.      * First get the desired position into the vertical range of the window.
  3017.      */
  3018.  
  3019.     TkTextSetYView(textPtr, &index, 1);
  3020.  
  3021.     /*
  3022.      * Now make sure that the character is in view horizontally.
  3023.      */
  3024.  
  3025.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3026.     UpdateDisplayInfo(textPtr);
  3027.     }
  3028.     lineWidth = dInfoPtr->maxX - dInfoPtr->x;
  3029.     if (dInfoPtr->maxLength < lineWidth) {
  3030.     return TCL_OK;
  3031.     }
  3032.  
  3033.     /*
  3034.      * Find the chunk that contains the desired index.
  3035.      */
  3036.  
  3037.     dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
  3038.     charCount = index.charIndex - dlPtr->index.charIndex;
  3039.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  3040.     if (charCount < chunkPtr->numChars) {
  3041.         break;
  3042.     }
  3043.     charCount -= chunkPtr->numChars;
  3044.     }
  3045.  
  3046.     /*
  3047.      * Call a chunk-specific procedure to find the horizontal range of
  3048.      * the character within the chunk.
  3049.      */
  3050.  
  3051.     (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y + dlPtr->spaceAbove,
  3052.         dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  3053.         dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
  3054.         &height);
  3055.     delta = x - dInfoPtr->curPixelOffset;
  3056.     oneThird = lineWidth/3;
  3057.     if (delta < 0) {
  3058.     if (delta < -oneThird) {
  3059.         dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
  3060.     } else {
  3061.         dInfoPtr->newCharOffset -= ((-delta) + textPtr->charWidth - 1)
  3062.         / textPtr->charWidth;
  3063.     }
  3064.     } else {
  3065.     delta -= (lineWidth - width);
  3066.     if (delta > 0) {
  3067.         if (delta > oneThird) {
  3068.         dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
  3069.         } else {
  3070.         dInfoPtr->newCharOffset += (delta + textPtr->charWidth - 1)
  3071.             / textPtr->charWidth;
  3072.         }
  3073.     } else {
  3074.         return TCL_OK;
  3075.     }
  3076.     }
  3077.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3078.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3079.     dInfoPtr->flags |= REDRAW_PENDING;
  3080.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3081.     }
  3082.     return TCL_OK;
  3083. }
  3084.  
  3085. /*
  3086.  *--------------------------------------------------------------
  3087.  *
  3088.  * TkTextXviewCmd --
  3089.  *
  3090.  *    This procedure is invoked to process the "xview" option for
  3091.  *    the widget command for text widgets. See the user documentation
  3092.  *    for details on what it does.
  3093.  *
  3094.  * Results:
  3095.  *    A standard Tcl result.
  3096.  *
  3097.  * Side effects:
  3098.  *    See the user documentation.
  3099.  *
  3100.  *--------------------------------------------------------------
  3101.  */
  3102.  
  3103. int
  3104. TkTextXviewCmd(textPtr, interp, argc, argv)
  3105.     TkText *textPtr;        /* Information about text widget. */
  3106.     Tcl_Interp *interp;        /* Current interpreter. */
  3107.     int argc;            /* Number of arguments. */
  3108.     char **argv;        /* Argument strings.  Someone else has already
  3109.                  * parsed this command enough to know that
  3110.                  * argv[1] is "xview". */
  3111. {
  3112.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3113.     int type, charsPerPage, count, newOffset;
  3114.     double fraction;
  3115.  
  3116.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3117.     UpdateDisplayInfo(textPtr);
  3118.     }
  3119.  
  3120.     if (argc == 2) {
  3121.     GetXView(interp, textPtr, 0);
  3122.     return TCL_OK;
  3123.     }
  3124.  
  3125.     newOffset = dInfoPtr->newCharOffset;
  3126.     type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  3127.     switch (type) {
  3128.     case TK_SCROLL_ERROR:
  3129.         return TCL_ERROR;
  3130.     case TK_SCROLL_MOVETO:
  3131.         newOffset = (fraction * dInfoPtr->maxLength) / textPtr->charWidth;
  3132.         break;
  3133.     case TK_SCROLL_PAGES:
  3134.         charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
  3135.             - 2;
  3136.         if (charsPerPage < 1) {
  3137.         charsPerPage = 1;
  3138.         }
  3139.         newOffset += charsPerPage*count;
  3140.         break;
  3141.     case TK_SCROLL_UNITS:
  3142.         newOffset += count;
  3143.         break;
  3144.     }
  3145.  
  3146.     dInfoPtr->newCharOffset = newOffset;
  3147.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3148.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3149.     dInfoPtr->flags |= REDRAW_PENDING;
  3150.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3151.     }
  3152.     return TCL_OK;
  3153. }
  3154.  
  3155. /*
  3156.  *----------------------------------------------------------------------
  3157.  *
  3158.  * ScrollByLines --
  3159.  *
  3160.  *    This procedure is called to scroll a text widget up or down
  3161.  *    by a given number of lines.
  3162.  *
  3163.  * Results:
  3164.  *    None.
  3165.  *
  3166.  * Side effects:
  3167.  *    The view in textPtr's window changes to reflect the value
  3168.  *    of "offset".
  3169.  *
  3170.  *----------------------------------------------------------------------
  3171.  */
  3172.  
  3173. static void
  3174. ScrollByLines(textPtr, offset)
  3175.     TkText *textPtr;        /* Widget to scroll. */
  3176.     int offset;            /* Amount by which to scroll, in *screen*
  3177.                  * lines.  Positive means that information
  3178.                  * later in text becomes visible, negative
  3179.                  * means that information earlier in the
  3180.                  * text becomes visible. */
  3181. {
  3182.     int i, charsToCount, lineNum;
  3183.     TkTextIndex new, index;
  3184.     TkTextLine *lastLinePtr;
  3185.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3186.     DLine *dlPtr, *lowestPtr;
  3187.  
  3188.     if (offset < 0) {
  3189.     /*
  3190.      * Must scroll up (to show earlier information in the text).
  3191.      * The code below is similar to that in MeasureUp, except that
  3192.      * it counts lines instead of pixels.
  3193.      */
  3194.  
  3195.     charsToCount = textPtr->topIndex.charIndex + 1;
  3196.     index.tree = textPtr->tree;
  3197.     offset--;            /* Skip line containing topIndex. */
  3198.     for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
  3199.         lineNum >= 0; lineNum--) {
  3200.         index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
  3201.         index.charIndex = 0;
  3202.         lowestPtr = NULL;
  3203.         do {
  3204.         dlPtr = LayoutDLine(textPtr, &index);
  3205.         dlPtr->nextPtr = lowestPtr;
  3206.         lowestPtr = dlPtr;
  3207.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  3208.         charsToCount -= dlPtr->count;
  3209.         } while ((charsToCount > 0)
  3210.             && (index.linePtr == dlPtr->index.linePtr));
  3211.  
  3212.         for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  3213.         offset++;
  3214.         if (offset == 0) {
  3215.             textPtr->topIndex = dlPtr->index;
  3216.             break;
  3217.         }
  3218.         }
  3219.     
  3220.         /*
  3221.          * Discard the display lines, then either return or prepare
  3222.          * for the next display line to lay out.
  3223.          */
  3224.     
  3225.         FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  3226.         if (offset >= 0) {
  3227.         goto scheduleUpdate;
  3228.         }
  3229.         charsToCount = INT_MAX;
  3230.     }
  3231.     
  3232.     /*
  3233.      * Ran off the beginning of the text.  Return the first character
  3234.      * in the text.
  3235.      */
  3236.  
  3237.     TkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
  3238.     } else {
  3239.     /*
  3240.      * Scrolling down, to show later information in the text.
  3241.      * Just count lines from the current top of the window.
  3242.      */
  3243.  
  3244.     lastLinePtr = TkBTreeFindLine(textPtr->tree,
  3245.         TkBTreeNumLines(textPtr->tree));
  3246.     for (i = 0; i < offset; i++) {
  3247.         dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
  3248.         dlPtr->nextPtr = NULL;
  3249.         TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
  3250.         FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  3251.         if (new.linePtr == lastLinePtr) {
  3252.         break;
  3253.         }
  3254.         textPtr->topIndex = new;
  3255.     }
  3256.     }
  3257.  
  3258.     scheduleUpdate:
  3259.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3260.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3261.     }
  3262.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  3263. }
  3264.  
  3265. /*
  3266.  *--------------------------------------------------------------
  3267.  *
  3268.  * TkTextYviewCmd --
  3269.  *
  3270.  *    This procedure is invoked to process the "yview" option for
  3271.  *    the widget command for text widgets. See the user documentation
  3272.  *    for details on what it does.
  3273.  *
  3274.  * Results:
  3275.  *    A standard Tcl result.
  3276.  *
  3277.  * Side effects:
  3278.  *    See the user documentation.
  3279.  *
  3280.  *--------------------------------------------------------------
  3281.  */
  3282.  
  3283. int
  3284. TkTextYviewCmd(textPtr, interp, argc, argv)
  3285.     TkText *textPtr;        /* Information about text widget. */
  3286.     Tcl_Interp *interp;        /* Current interpreter. */
  3287.     int argc;            /* Number of arguments. */
  3288.     char **argv;        /* Argument strings.  Someone else has already
  3289.                  * parsed this command enough to know that
  3290.                  * argv[1] is "yview". */
  3291. {
  3292.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3293.     int pickPlace, lineNum, type, lineHeight;
  3294.     int pixels, count;
  3295.     size_t switchLength;
  3296.     double fraction;
  3297.     TkTextIndex index, new;
  3298.     TkTextLine *lastLinePtr;
  3299.     DLine *dlPtr;
  3300.  
  3301.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3302.     UpdateDisplayInfo(textPtr);
  3303.     }
  3304.  
  3305.     if (argc == 2) {
  3306.     GetYView(interp, textPtr, 0);
  3307.     return TCL_OK;
  3308.     }
  3309.  
  3310.     /*
  3311.      * Next, handle the old syntax: "pathName yview ?-pickplace? where"
  3312.      */
  3313.  
  3314.     pickPlace = 0;
  3315.     if (argv[2][0] == '-') {
  3316.     switchLength = strlen(argv[2]);
  3317.     if ((switchLength >= 2)
  3318.         && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
  3319.         pickPlace = 1;
  3320.         if (argc != 4) {
  3321.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3322.             argv[0], " yview -pickplace lineNum|index\"",
  3323.             (char *) NULL);
  3324.         return TCL_ERROR;
  3325.         }
  3326.     }
  3327.     }
  3328.     if ((argc == 3) || pickPlace) {
  3329.     if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
  3330.         TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
  3331.         TkTextSetYView(textPtr, &index, 0);
  3332.         return TCL_OK;
  3333.     }
  3334.     
  3335.     /*
  3336.      * The argument must be a regular text index.
  3337.      */
  3338.     
  3339.     Tcl_ResetResult(interp);
  3340.     if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
  3341.         &index) != TCL_OK) {
  3342.         return TCL_ERROR;
  3343.     }
  3344.     TkTextSetYView(textPtr, &index, pickPlace);
  3345.     return TCL_OK;
  3346.     }
  3347.  
  3348.     /*
  3349.      * New syntax: dispatch based on argv[2].
  3350.      */
  3351.  
  3352.     type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  3353.     switch (type) {
  3354.     case TK_SCROLL_ERROR:
  3355.         return TCL_ERROR;
  3356.     case TK_SCROLL_MOVETO:
  3357.         fraction *= TkBTreeNumLines(textPtr->tree);
  3358.         lineNum = fraction;
  3359.         TkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
  3360.         TkTextIndexBackChars(&index, 1, &index);
  3361.         index.charIndex = (index.charIndex+1)*(fraction-lineNum);
  3362.         TkTextSetYView(textPtr, &index, 0);
  3363.         break;
  3364.     case TK_SCROLL_PAGES:
  3365.         /*
  3366.          * Scroll up or down by screenfulls.  Actually, use the
  3367.          * window height minus two lines, so that there's some
  3368.          * overlap between adjacent pages.
  3369.          */
  3370.  
  3371.         lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent;
  3372.         if (count < 0) {
  3373.         pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*(-count)
  3374.             + lineHeight;
  3375.         MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
  3376.         if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
  3377.             /*
  3378.              * A page of scrolling ended up being less than one line.
  3379.              * Scroll one line anyway.
  3380.              */
  3381.  
  3382.             count = -1;
  3383.             goto scrollByLines;
  3384.         }
  3385.         textPtr->topIndex = new;
  3386.         } else {
  3387.         /*
  3388.          * Scrolling down by pages.  Layout lines starting at the
  3389.          * top index and count through the desired vertical distance.
  3390.          */
  3391.  
  3392.         pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*count;
  3393.         lastLinePtr = TkBTreeFindLine(textPtr->tree,
  3394.             TkBTreeNumLines(textPtr->tree));
  3395.         do {
  3396.             dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
  3397.             dlPtr->nextPtr = NULL;
  3398.             TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
  3399.                 &new);
  3400.             pixels -= dlPtr->height;
  3401.             FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  3402.             if (new.linePtr == lastLinePtr) {
  3403.             break;
  3404.             }
  3405.             textPtr->topIndex = new;
  3406.         } while (pixels > 0);
  3407.         }
  3408.         if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3409.         Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3410.         }
  3411.         dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  3412.         break;
  3413.     case TK_SCROLL_UNITS:
  3414.         scrollByLines:
  3415.         ScrollByLines(textPtr, count);
  3416.         break;
  3417.     }
  3418.     return TCL_OK;
  3419. }
  3420.  
  3421. /*
  3422.  *--------------------------------------------------------------
  3423.  *
  3424.  * TkTextScanCmd --
  3425.  *
  3426.  *    This procedure is invoked to process the "scan" option for
  3427.  *    the widget command for text widgets. See the user documentation
  3428.  *    for details on what it does.
  3429.  *
  3430.  * Results:
  3431.  *    A standard Tcl result.
  3432.  *
  3433.  * Side effects:
  3434.  *    See the user documentation.
  3435.  *
  3436.  *--------------------------------------------------------------
  3437.  */
  3438.  
  3439. int
  3440. TkTextScanCmd(textPtr, interp, argc, argv)
  3441.     register TkText *textPtr;    /* Information about text widget. */
  3442.     Tcl_Interp *interp;        /* Current interpreter. */
  3443.     int argc;            /* Number of arguments. */
  3444.     char **argv;        /* Argument strings.  Someone else has already
  3445.                  * parsed this command enough to know that
  3446.                  * argv[1] is "scan". */
  3447. {
  3448.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3449.     TkTextIndex index;
  3450.     int c, x, y, totalScroll, newChar, maxChar;
  3451.     size_t length;
  3452.  
  3453.     if (argc != 5) {
  3454.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  3455.         argv[0], " scan mark|dragto x y\"", (char *) NULL);
  3456.     return TCL_ERROR;
  3457.     }
  3458.     if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
  3459.     return TCL_ERROR;
  3460.     }
  3461.     if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
  3462.     return TCL_ERROR;
  3463.     }
  3464.     c = argv[2][0];
  3465.     length = strlen(argv[2]);
  3466.     if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
  3467.     /*
  3468.      * Amplify the difference between the current position and the
  3469.      * mark position to compute how much the view should shift, then
  3470.      * update the mark position to correspond to the new view.  If we
  3471.      * run off the edge of the text, reset the mark point so that the
  3472.      * current position continues to correspond to the edge of the
  3473.      * window.  This means that the picture will start dragging as
  3474.      * soon as the mouse reverses direction (without this reset, might
  3475.      * have to slide mouse a long ways back before the picture starts
  3476.      * moving again).
  3477.      */
  3478.  
  3479.     newChar = dInfoPtr->scanMarkChar + (10*(dInfoPtr->scanMarkX - x))
  3480.         / (textPtr->charWidth);
  3481.     maxChar = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
  3482.         + textPtr->charWidth - 1)/textPtr->charWidth;
  3483.     if (newChar < 0) {
  3484.         dInfoPtr->scanMarkChar = newChar = 0;
  3485.         dInfoPtr->scanMarkX = x;
  3486.     } else if (newChar > maxChar) {
  3487.         dInfoPtr->scanMarkChar = newChar = maxChar;
  3488.         dInfoPtr->scanMarkX = x;
  3489.     }
  3490.     dInfoPtr->newCharOffset = newChar;
  3491.  
  3492.     totalScroll = (10*(dInfoPtr->scanMarkY - y))
  3493.         / (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
  3494.     if (totalScroll != dInfoPtr->scanTotalScroll) {
  3495.         index = textPtr->topIndex;
  3496.         ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
  3497.         dInfoPtr->scanTotalScroll = totalScroll;
  3498.         if ((index.linePtr == textPtr->topIndex.linePtr) &&
  3499.             (index.charIndex == textPtr->topIndex.charIndex)) {
  3500.         dInfoPtr->scanTotalScroll = 0;
  3501.         dInfoPtr->scanMarkY = y;
  3502.         }
  3503.     }
  3504.     } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
  3505.     dInfoPtr->scanMarkChar = dInfoPtr->newCharOffset;
  3506.     dInfoPtr->scanMarkX = x;
  3507.     dInfoPtr->scanTotalScroll = 0;
  3508.     dInfoPtr->scanMarkY = y;
  3509.     } else {
  3510.     Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  3511.         "\":  must be mark or dragto", (char *) NULL);
  3512.     return TCL_ERROR;
  3513.     }
  3514.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3515.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3516.     dInfoPtr->flags |= REDRAW_PENDING;
  3517.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3518.     }
  3519.     return TCL_OK;
  3520. }
  3521.  
  3522. /*
  3523.  *----------------------------------------------------------------------
  3524.  *
  3525.  * GetXView --
  3526.  *
  3527.  *    This procedure computes the fractions that indicate what's
  3528.  *    visible in a text window and, optionally, evaluates a
  3529.  *    Tcl script to report them to the text's associated scrollbar.
  3530.  *
  3531.  * Results:
  3532.  *    If report is zero, then interp->result is filled in with
  3533.  *    two real numbers separated by a space, giving the position of
  3534.  *    the left and right edges of the window as fractions from 0 to
  3535.  *    1, where 0 means the left edge of the text and 1 means the right
  3536.  *    edge.  If report is non-zero, then interp->result isn't modified
  3537.  *    directly, but instead a script is evaluated in interp to report
  3538.  *    the new horizontal scroll position to the scrollbar (if the scroll
  3539.  *    position hasn't changed then no script is invoked).
  3540.  *
  3541.  * Side effects:
  3542.  *    None.
  3543.  *
  3544.  *----------------------------------------------------------------------
  3545.  */
  3546.  
  3547. static void
  3548. GetXView(interp, textPtr, report)
  3549.     Tcl_Interp *interp;            /* If "report" is FALSE, string
  3550.                      * describing visible range gets
  3551.                      * stored in interp->result. */
  3552.     TkText *textPtr;            /* Information about text widget. */
  3553.     int report;                /* Non-zero means report info to
  3554.                      * scrollbar if it has changed. */
  3555. {
  3556.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3557.     char buffer[200];
  3558.     double first, last;
  3559.     int code;
  3560.  
  3561.     if (dInfoPtr->maxLength > 0) {
  3562.     first = ((double) dInfoPtr->curPixelOffset)
  3563.         / dInfoPtr->maxLength;
  3564.     last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
  3565.         / dInfoPtr->maxLength;
  3566.     if (last > 1.0) {
  3567.         last = 1.0;
  3568.     }
  3569.     } else {
  3570.     first = 0;
  3571.     last = 1.0;
  3572.     }
  3573.     if (!report) {
  3574.     sprintf(interp->result, "%g %g", first, last);
  3575.     return;
  3576.     }
  3577.     if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
  3578.     return;
  3579.     }
  3580.     dInfoPtr->xScrollFirst = first;
  3581.     dInfoPtr->xScrollLast = last;
  3582.     sprintf(buffer, " %g %g", first, last);
  3583.     code = Tcl_VarEval(interp, textPtr->xScrollCmd,
  3584.         buffer, (char *) NULL);
  3585.     if (code != TCL_OK) {
  3586.     Tcl_AddErrorInfo(interp,
  3587.         "\n    (horizontal scrolling command executed by text)");
  3588.     Tk_BackgroundError(interp);
  3589.     }
  3590. }
  3591.  
  3592. /*
  3593.  *----------------------------------------------------------------------
  3594.  *
  3595.  * GetYView --
  3596.  *
  3597.  *    This procedure computes the fractions that indicate what's
  3598.  *    visible in a text window and, optionally, evaluates a
  3599.  *    Tcl script to report them to the text's associated scrollbar.
  3600.  *
  3601.  * Results:
  3602.  *    If report is zero, then interp->result is filled in with
  3603.  *    two real numbers separated by a space, giving the position of
  3604.  *    the top and bottom of the window as fractions from 0 to 1, where
  3605.  *    0 means the beginning of the text and 1 means the end.  If
  3606.  *    report is non-zero, then interp->result isn't modified directly,
  3607.  *    but a script is evaluated in interp to report the new scroll
  3608.  *    position to the scrollbar (if the scroll position hasn't changed
  3609.  *    then no script is invoked).
  3610.  *
  3611.  * Side effects:
  3612.  *    None.
  3613.  *
  3614.  *----------------------------------------------------------------------
  3615.  */
  3616.  
  3617. static void
  3618. GetYView(interp, textPtr, report)
  3619.     Tcl_Interp *interp;            /* If "report" is FALSE, string
  3620.                      * describing visible range gets
  3621.                      * stored in interp->result. */
  3622.     TkText *textPtr;            /* Information about text widget. */
  3623.     int report;                /* Non-zero means report info to
  3624.                      * scrollbar if it has changed. */
  3625. {
  3626.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3627.     char buffer[200];
  3628.     double first, last;
  3629.     DLine *dlPtr;
  3630.     int totalLines, code, count;
  3631.  
  3632.     dlPtr = dInfoPtr->dLinePtr;
  3633.     totalLines = TkBTreeNumLines(textPtr->tree);
  3634.     first = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
  3635.         + ((double) dlPtr->index.charIndex)
  3636.         / (TkBTreeCharsInLine(dlPtr->index.linePtr));
  3637.     first /= totalLines;
  3638.     while (1) {
  3639.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  3640.         /*
  3641.          * The last line is only partially visible, so don't
  3642.          * count its characters in what's visible.
  3643.          */
  3644.         count = 0;
  3645.         break;
  3646.     }
  3647.     if (dlPtr->nextPtr == NULL) {
  3648.         count = dlPtr->count;
  3649.         break;
  3650.     }
  3651.     dlPtr = dlPtr->nextPtr;
  3652.     }
  3653.     last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
  3654.         + ((double) (dlPtr->index.charIndex + count))
  3655.         / (TkBTreeCharsInLine(dlPtr->index.linePtr));
  3656.     last /= totalLines;
  3657.     if (!report) {
  3658.     sprintf(interp->result, "%g %g", first, last);
  3659.     return;
  3660.     }
  3661.     if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
  3662.     return;
  3663.     }
  3664.     dInfoPtr->yScrollFirst = first;
  3665.     dInfoPtr->yScrollLast = last;
  3666.     sprintf(buffer, " %g %g", first, last);
  3667.     code = Tcl_VarEval(interp, textPtr->yScrollCmd,
  3668.         buffer, (char *) NULL);
  3669.     if (code != TCL_OK) {
  3670.     Tcl_AddErrorInfo(interp,
  3671.         "\n    (vertical scrolling command executed by text)");
  3672.     Tk_BackgroundError(interp);
  3673.     }
  3674. }
  3675.  
  3676. /*
  3677.  *----------------------------------------------------------------------
  3678.  *
  3679.  * FindDLine --
  3680.  *
  3681.  *    This procedure is called to find the DLine corresponding to a
  3682.  *    given text index.
  3683.  *
  3684.  * Results:
  3685.  *    The return value is a pointer to the first DLine found in the
  3686.  *    list headed by dlPtr that displays information at or after the
  3687.  *    specified position.  If there is no such line in the list then
  3688.  *    NULL is returned.
  3689.  *
  3690.  * Side effects:
  3691.  *    None.
  3692.  *
  3693.  *----------------------------------------------------------------------
  3694.  */
  3695.  
  3696. static DLine *
  3697. FindDLine(dlPtr, indexPtr)
  3698.     register DLine *dlPtr;    /* Pointer to first in list of DLines
  3699.                  * to search. */
  3700.     TkTextIndex *indexPtr;    /* Index of desired character. */
  3701. {
  3702.     TkTextLine *linePtr;
  3703.  
  3704.     if (dlPtr == NULL) {
  3705.     return NULL;
  3706.     }
  3707.     if (TkBTreeLineIndex(indexPtr->linePtr)
  3708.         < TkBTreeLineIndex(dlPtr->index.linePtr)) {
  3709.     /*
  3710.      * The first display line is already past the desired line.
  3711.      */
  3712.     return dlPtr;
  3713.     }
  3714.  
  3715.     /*
  3716.      * Find the first display line that covers the desired text line.
  3717.      */
  3718.  
  3719.     linePtr = dlPtr->index.linePtr;
  3720.     while (linePtr != indexPtr->linePtr) {
  3721.     while (dlPtr->index.linePtr == linePtr) {
  3722.         dlPtr = dlPtr->nextPtr;
  3723.         if (dlPtr == NULL) {
  3724.         return NULL;
  3725.         }
  3726.     }
  3727.     linePtr = TkBTreeNextLine(linePtr);
  3728.     if (linePtr == NULL) {
  3729.         panic("FindDLine reached end of text");
  3730.     }
  3731.     }
  3732.     if (indexPtr->linePtr != dlPtr->index.linePtr) {
  3733.     return dlPtr;
  3734.     }
  3735.  
  3736.     /*
  3737.      * Now get to the right position within the text line.
  3738.      */
  3739.  
  3740.     while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
  3741.     dlPtr = dlPtr->nextPtr;
  3742.     if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
  3743.         break;
  3744.     }
  3745.     }
  3746.     return dlPtr;
  3747. }
  3748.  
  3749. /*
  3750.  *----------------------------------------------------------------------
  3751.  *
  3752.  * TkTextPixelIndex --
  3753.  *
  3754.  *    Given an (x,y) coordinate on the screen, find the location of
  3755.  *    the character closest to that location.
  3756.  *
  3757.  * Results:
  3758.  *    The index at *indexPtr is modified to refer to the character
  3759.  *    on the display that is closest to (x,y).
  3760.  *
  3761.  * Side effects:
  3762.  *    None.
  3763.  *
  3764.  *----------------------------------------------------------------------
  3765.  */
  3766.  
  3767. void
  3768. TkTextPixelIndex(textPtr, x, y, indexPtr)
  3769.     TkText *textPtr;        /* Widget record for text widget. */
  3770.     int x, y;            /* Pixel coordinates of point in widget's
  3771.                  * window. */
  3772.     TkTextIndex *indexPtr;    /* This index gets filled in with the
  3773.                  * index of the character nearest to (x,y). */
  3774. {
  3775.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3776.     register DLine *dlPtr;
  3777.     register TkTextDispChunk *chunkPtr;
  3778.  
  3779.     /*
  3780.      * Make sure that all of the layout information about what's
  3781.      * displayed where on the screen is up-to-date.
  3782.      */
  3783.  
  3784.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3785.     UpdateDisplayInfo(textPtr);
  3786.     }
  3787.  
  3788.     /*
  3789.      * If the coordinates are above the top of the window, then adjust
  3790.      * them to refer to the upper-right corner of the window.  If they're
  3791.      * off to one side or the other, then adjust to the closest side.
  3792.      */
  3793.  
  3794.     if (y < dInfoPtr->y) {
  3795.     y = dInfoPtr->y;
  3796.     x = dInfoPtr->x;
  3797.     }
  3798.     if (x >= dInfoPtr->maxX) {
  3799.     x = dInfoPtr->maxX - 1;
  3800.     }
  3801.     if (x < dInfoPtr->x) {
  3802.     x = dInfoPtr->x;
  3803.     }
  3804.  
  3805.     /*
  3806.      * Find the display line containing the desired y-coordinate.
  3807.      */
  3808.  
  3809.     for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
  3810.         dlPtr = dlPtr->nextPtr) {
  3811.     if (dlPtr->nextPtr == NULL) {
  3812.         /*
  3813.          * Y-coordinate is off the bottom of the displayed text.
  3814.          * Use the last character on the last line.
  3815.          */
  3816.  
  3817.         x = dInfoPtr->maxX - 1;
  3818.         break;
  3819.     }
  3820.     }
  3821.  
  3822.     /*
  3823.      * Scan through the line's chunks to find the one that contains
  3824.      * the desired x-coordinate.  Before doing this, translate the
  3825.      * x-coordinate from the coordinate system of the window to the
  3826.      * coordinate system of the line (to take account of x-scrolling).
  3827.      */
  3828.  
  3829.     *indexPtr = dlPtr->index;
  3830.     x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
  3831.     for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
  3832.         indexPtr->charIndex += chunkPtr->numChars,
  3833.         chunkPtr = chunkPtr->nextPtr) {
  3834.     if (chunkPtr->nextPtr == NULL) {
  3835.         indexPtr->charIndex += chunkPtr->numChars - 1;
  3836.         return;
  3837.     }
  3838.     }
  3839.  
  3840.     /*
  3841.      * If the chunk has more than one character in it, ask it which
  3842.      * character is at the desired location.
  3843.      */
  3844.  
  3845.     if (chunkPtr->numChars > 1) {
  3846.     indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
  3847.     }
  3848. }
  3849.  
  3850. /*
  3851.  *----------------------------------------------------------------------
  3852.  *
  3853.  * TkTextCharBbox --
  3854.  *
  3855.  *    Given an index, find the bounding box of the screen area
  3856.  *    occupied by that character.
  3857.  *
  3858.  * Results:
  3859.  *    Zero is returned if the character is on the screen.  -1
  3860.  *    means the character isn't on the screen.  If the return value
  3861.  *    is 0, then the bounding box of the part of the character that's
  3862.  *    visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
  3863.  *    and *heightPtr.
  3864.  *
  3865.  * Side effects:
  3866.  *    None.
  3867.  *
  3868.  *----------------------------------------------------------------------
  3869.  */
  3870.  
  3871. int
  3872. TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
  3873.     TkText *textPtr;        /* Widget record for text widget. */
  3874.     TkTextIndex *indexPtr;    /* Index of character whose bounding
  3875.                  * box is desired. */
  3876.     int *xPtr, *yPtr;        /* Filled with character's upper-left
  3877.                  * coordinate. */
  3878.     int *widthPtr, *heightPtr;    /* Filled in with character's dimensions. */
  3879. {
  3880.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3881.     DLine *dlPtr;
  3882.     register TkTextDispChunk *chunkPtr;
  3883.     int index;
  3884.  
  3885.     /*
  3886.      * Make sure that all of the screen layout information is up to date.
  3887.      */
  3888.  
  3889.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3890.     UpdateDisplayInfo(textPtr);
  3891.     }
  3892.  
  3893.     /*
  3894.      * Find the display line containing the desired index.
  3895.      */
  3896.  
  3897.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  3898.     if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
  3899.     return -1;
  3900.     }
  3901.  
  3902.     /*
  3903.      * Find the chunk within the line that contains the desired
  3904.      * index.
  3905.      */
  3906.  
  3907.     index = indexPtr->charIndex - dlPtr->index.charIndex;
  3908.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  3909.     if (chunkPtr == NULL) {
  3910.         return -1;
  3911.     }
  3912.     if (index < chunkPtr->numChars) {
  3913.         break;
  3914.     }
  3915.     index -= chunkPtr->numChars;
  3916.     }
  3917.  
  3918.     /*
  3919.      * Call a chunk-specific procedure to find the horizontal range of
  3920.      * the character within the chunk, then fill in the vertical range.
  3921.      * The x-coordinate returned by bboxProc is a coordinate within a
  3922.      * line, not a coordinate on the screen.  Translate it to reflect
  3923.      * horizontal scrolling.
  3924.      */
  3925.  
  3926.     (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y + dlPtr->spaceAbove,
  3927.         dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  3928.         dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
  3929.         heightPtr);
  3930.     *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
  3931.     if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
  3932.     /*
  3933.      * Last character in display line.  Give it all the space up to
  3934.      * the line.
  3935.      */
  3936.  
  3937.     if (*xPtr > dInfoPtr->maxX) {
  3938.         *xPtr = dInfoPtr->maxX;
  3939.     }
  3940.     *widthPtr = dInfoPtr->maxX - *xPtr;
  3941.     }
  3942.     if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
  3943.     return -1;
  3944.     }
  3945.     if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
  3946.     *widthPtr = dInfoPtr->maxX - *xPtr;
  3947.     if (*widthPtr <= 0) {
  3948.         return -1;
  3949.     }
  3950.     }
  3951.     if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
  3952.     *heightPtr = dInfoPtr->maxY - *yPtr;
  3953.     if (*heightPtr <= 0) {
  3954.         return -1;
  3955.     }
  3956.     }
  3957.     return 0;
  3958. }
  3959.  
  3960. /*
  3961.  *----------------------------------------------------------------------
  3962.  *
  3963.  * TkTextDLineInfo --
  3964.  *
  3965.  *    Given an index, return information about the display line
  3966.  *    containing that character.
  3967.  *
  3968.  * Results:
  3969.  *    Zero is returned if the character is on the screen.  -1
  3970.  *    means the character isn't on the screen.  If the return value
  3971.  *    is 0, then information is returned in the variables pointed
  3972.  *    to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
  3973.  *
  3974.  * Side effects:
  3975.  *    None.
  3976.  *
  3977.  *----------------------------------------------------------------------
  3978.  */
  3979.  
  3980. int
  3981. TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
  3982.     TkText *textPtr;        /* Widget record for text widget. */
  3983.     TkTextIndex *indexPtr;    /* Index of character whose bounding
  3984.                  * box is desired. */
  3985.     int *xPtr, *yPtr;        /* Filled with line's upper-left
  3986.                  * coordinate. */
  3987.     int *widthPtr, *heightPtr;    /* Filled in with line's dimensions. */
  3988.     int *basePtr;        /* Filled in with the baseline position,
  3989.                  * measured as an offset down from *yPtr. */
  3990. {
  3991.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  3992.     DLine *dlPtr;
  3993.  
  3994.     /*
  3995.      * Make sure that all of the screen layout information is up to date.
  3996.      */
  3997.  
  3998.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3999.     UpdateDisplayInfo(textPtr);
  4000.     }
  4001.  
  4002.     /*
  4003.      * Find the display line containing the desired index.
  4004.      */
  4005.  
  4006.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  4007.     if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
  4008.     return -1;
  4009.     }
  4010.  
  4011.     *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlPtr->chunkPtr->x;
  4012.     *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
  4013.     *yPtr = dlPtr->y;
  4014.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  4015.     *heightPtr = dInfoPtr->maxY - dlPtr->y;
  4016.     } else {
  4017.     *heightPtr = dlPtr->height;
  4018.     }
  4019.     *basePtr = dlPtr->baseline;
  4020.     return 0;
  4021. }
  4022.  
  4023. /*
  4024.  *--------------------------------------------------------------
  4025.  *
  4026.  * TkTextCharLayoutProc --
  4027.  *
  4028.  *    This procedure is the "layoutProc" for character segments.
  4029.  *
  4030.  * Results:
  4031.  *    If there is something to display for the chunk then a
  4032.  *    non-zero value is returned and the fields of chunkPtr
  4033.  *    will be filled in (see the declaration of TkTextDispChunk
  4034.  *    in tkText.h for details).  If zero is returned it means
  4035.  *    that no characters from this chunk fit in the window.
  4036.  *    If -1 is returned it means that this segment just doesn't
  4037.  *    need to be displayed (never happens for text).
  4038.  *
  4039.  * Side effects:
  4040.  *    Memory is allocated to hold additional information about
  4041.  *    the chunk.
  4042.  *
  4043.  *--------------------------------------------------------------
  4044.  */
  4045.  
  4046. int
  4047. TkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
  4048.     noCharsYet, wrapMode, chunkPtr)
  4049.     TkText *textPtr;        /* Text widget being layed out. */
  4050.     TkTextIndex *indexPtr;    /* Index of first character to lay out
  4051.                  * (corresponds to segPtr and offset). */
  4052.     TkTextSegment *segPtr;    /* Segment being layed out. */
  4053.     int offset;            /* Offset within segment of first character
  4054.                  * to consider. */
  4055.     int maxX;            /* Chunk must not occupy pixels at this
  4056.                  * position or higher. */
  4057.     int maxChars;        /* Chunk must not include more than this
  4058.                  * many characters. */
  4059.     int noCharsYet;        /* Non-zero means no characters have been
  4060.                  * assigned to this display line yet. */
  4061.     Tk_Uid wrapMode;        /* How to handle line wrapping: tkTextCharUid,
  4062.                  * tkTextNoneUid, or tkTextWordUid. */
  4063.     register TkTextDispChunk *chunkPtr;
  4064.                 /* Structure to fill in with information
  4065.                  * about this chunk.  The x field has already
  4066.                  * been set by the caller. */
  4067. {
  4068.     XFontStruct *fontPtr;
  4069.     int nextX, charsThatFit, count;
  4070.     CharInfo *ciPtr;
  4071.     char *p;
  4072.     TkTextSegment *nextPtr;
  4073.  
  4074.     /*
  4075.      * Figure out how many characters will fit in the space we've got.
  4076.      * Include the next character, even though it won't fit completely,
  4077.      * if any of the following is true:
  4078.      *   (a) the chunk contains no characters and the display line contains
  4079.      *         no characters yet (i.e. the line isn't wide enough to hold
  4080.      *         even a single character).
  4081.      *   (b) at least one pixel of the character is visible, we haven't
  4082.      *         already exceeded the character limit, and the next character
  4083.      *         is a white space character.
  4084.      */
  4085.  
  4086.     p = segPtr->body.chars + offset;
  4087.     fontPtr = chunkPtr->stylePtr->sValuePtr->fontPtr;
  4088.     charsThatFit = TkMeasureChars(fontPtr, p, maxChars, chunkPtr->x,
  4089.         maxX, 0, TK_IGNORE_TABS, &nextX);
  4090.     if (charsThatFit < maxChars) {
  4091.     if ((charsThatFit == 0) && noCharsYet) {
  4092.         charsThatFit = 1;
  4093.         TkMeasureChars(fontPtr, p, 1, chunkPtr->x, INT_MAX, 0,
  4094.             TK_IGNORE_TABS, &nextX);
  4095.     }
  4096.     if (p[charsThatFit] == '\n') {
  4097.         /*
  4098.          * A newline character takes up no space, so if the previous
  4099.          * character fits then so does the newline.
  4100.          */
  4101.  
  4102.         charsThatFit++;
  4103.     } else if ((nextX < maxX) && (isspace(UCHAR(p[charsThatFit])))) {
  4104.         /*
  4105.          * Space characters are funny, in that they are considered
  4106.          * to fit if there is at least one pixel of space left on the
  4107.          * line.  Just give the space character whatever space is left.
  4108.          */
  4109.  
  4110.         nextX = maxX;
  4111.         charsThatFit++;
  4112.     }
  4113.     if (charsThatFit == 0) {
  4114.         return 0;
  4115.     }
  4116.     }
  4117.  
  4118.     /*
  4119.      * Fill in the chunk structure and allocate and initialize a
  4120.      * CharInfo structure.  If the last character is a newline
  4121.      * then don't bother to display it.
  4122.      */
  4123.  
  4124.     chunkPtr->displayProc = CharDisplayProc;
  4125.     chunkPtr->undisplayProc = CharUndisplayProc;
  4126.     chunkPtr->measureProc = CharMeasureProc;
  4127.     chunkPtr->bboxProc = CharBboxProc;
  4128.     chunkPtr->numChars = charsThatFit;
  4129.     chunkPtr->minAscent = fontPtr->ascent
  4130.         + chunkPtr->stylePtr->sValuePtr->offset;
  4131.     chunkPtr->minDescent = fontPtr->descent
  4132.         - chunkPtr->stylePtr->sValuePtr->offset;;
  4133.     chunkPtr->minHeight = 0;
  4134.     chunkPtr->width = nextX - chunkPtr->x;
  4135.     chunkPtr->breakIndex = -1;
  4136.     ciPtr = (CharInfo *) ckalloc((unsigned)
  4137.         (sizeof(CharInfo) - 3 + charsThatFit));
  4138.     chunkPtr->clientData = (ClientData) ciPtr;
  4139.     ciPtr->numChars = charsThatFit;
  4140.     strncpy(ciPtr->chars, p, (size_t) charsThatFit);
  4141.     if (p[charsThatFit-1] == '\n') {
  4142.     ciPtr->numChars--;
  4143.     }
  4144.  
  4145.     /*
  4146.      * Compute a break location.  If we're in word wrap mode, a
  4147.      * break can occur after any space character, or at the end of
  4148.      * the chunk if the next segment (ignoring those with zero size)
  4149.      * is not a character segment.
  4150.      */
  4151.  
  4152.     if (wrapMode != tkTextWordUid) {
  4153.     chunkPtr->breakIndex = chunkPtr->numChars;
  4154.     } else {
  4155.     for (count = charsThatFit, p += charsThatFit-1; count > 0;
  4156.         count--, p--) {
  4157.         if (isspace(UCHAR(*p))) {
  4158.         chunkPtr->breakIndex = count;
  4159.         break;
  4160.         }
  4161.     }
  4162.     if ((charsThatFit+offset) == segPtr->size) {
  4163.         for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
  4164.             nextPtr = nextPtr->nextPtr) {
  4165.         if (nextPtr->size != 0) {
  4166.             if (nextPtr->typePtr != &tkTextCharType) {
  4167.             chunkPtr->breakIndex = chunkPtr->numChars;
  4168.             }
  4169.             break;
  4170.         }
  4171.         }
  4172.     }
  4173.     }
  4174.     return 1;
  4175. }
  4176.  
  4177. /*
  4178.  *--------------------------------------------------------------
  4179.  *
  4180.  * CharDisplayProc --
  4181.  *
  4182.  *    This procedure is called to display a character chunk on
  4183.  *    the screen or in an off-screen pixmap.
  4184.  *
  4185.  * Results:
  4186.  *    None.
  4187.  *
  4188.  * Side effects:
  4189.  *    Graphics are drawn.
  4190.  *
  4191.  *--------------------------------------------------------------
  4192.  */
  4193.  
  4194. static void
  4195. CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
  4196.     TkTextDispChunk *chunkPtr;        /* Chunk that is to be drawn. */
  4197.     int x;                /* X-position in dst at which to
  4198.                      * draw this chunk (may differ from
  4199.                      * the x-position in the chunk because
  4200.                      * of scrolling). */
  4201.     int y;                /* Y-position at which to draw this
  4202.                      * chunk in dst. */
  4203.     int height;                /* Total height of line. */
  4204.     int baseline;            /* Offset of baseline from y. */
  4205.     Display *display;            /* Display to use for drawing. */
  4206.     Drawable dst;            /* Pixmap or window in which to draw
  4207.                      * chunk. */
  4208.     int screenY;            /* Y-coordinate in text window that
  4209.                      * corresponds to y. */
  4210. {
  4211.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4212.     Style *stylePtr;
  4213.     StyleValues *sValuePtr;
  4214.  
  4215.     if ((x + chunkPtr->width) <= 0) {
  4216.     /*
  4217.      * The chunk is off-screen.
  4218.      */
  4219.  
  4220.     return;
  4221.     }
  4222.  
  4223.     stylePtr = chunkPtr->stylePtr;
  4224.     sValuePtr = stylePtr->sValuePtr;
  4225.  
  4226.     /*
  4227.      * Draw the text, underline, and overstrike for this chunk.
  4228.      */
  4229.  
  4230.     if (ciPtr->numChars > 0) {
  4231.     TkDisplayChars(display, dst, stylePtr->fgGC, sValuePtr->fontPtr,
  4232.         ciPtr->chars, ciPtr->numChars, x,
  4233.         y + baseline - sValuePtr->offset, x - chunkPtr->x,
  4234.         TK_IGNORE_TABS);
  4235.     if (sValuePtr->underline) {
  4236.         TkUnderlineChars(display, dst, stylePtr->fgGC,
  4237.             sValuePtr->fontPtr, ciPtr->chars, x,
  4238.             y + baseline - sValuePtr->offset, x - chunkPtr->x,
  4239.             TK_IGNORE_TABS, 0, ciPtr->numChars-1);
  4240.     }
  4241.     if (sValuePtr->overstrike) {
  4242.         TkUnderlineChars(display, dst, stylePtr->fgGC,
  4243.             sValuePtr->fontPtr, ciPtr->chars, x,
  4244.             y + baseline - sValuePtr->offset
  4245.             - sValuePtr->fontPtr->descent
  4246.             - (sValuePtr->fontPtr->ascent*3)/10,
  4247.             x - chunkPtr->x, TK_IGNORE_TABS, 0, ciPtr->numChars-1);
  4248.     }
  4249.     }
  4250. }
  4251.  
  4252. /*
  4253.  *--------------------------------------------------------------
  4254.  *
  4255.  * CharUndisplayProc --
  4256.  *
  4257.  *    This procedure is called when a character chunk is no
  4258.  *    longer going to be displayed.  It frees up resources
  4259.  *    that were allocated to display the chunk.
  4260.  *
  4261.  * Results:
  4262.  *    None.
  4263.  *
  4264.  * Side effects:
  4265.  *    Memory and other resources get freed.
  4266.  *
  4267.  *--------------------------------------------------------------
  4268.  */
  4269.  
  4270. static void
  4271. CharUndisplayProc(textPtr, chunkPtr)
  4272.     TkText *textPtr;            /* Overall information about text
  4273.                      * widget. */
  4274.     TkTextDispChunk *chunkPtr;        /* Chunk that is about to be freed. */
  4275. {
  4276.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4277.  
  4278.     ckfree((char *) ciPtr);
  4279. }
  4280.  
  4281. /*
  4282.  *--------------------------------------------------------------
  4283.  *
  4284.  * CharMeasureProc --
  4285.  *
  4286.  *    This procedure is called to determine which character in
  4287.  *    a character chunk lies over a given x-coordinate.
  4288.  *
  4289.  * Results:
  4290.  *    The return value is the index *within the chunk* of the
  4291.  *    character that covers the position given by "x".
  4292.  *
  4293.  * Side effects:
  4294.  *    None.
  4295.  *
  4296.  *--------------------------------------------------------------
  4297.  */
  4298.  
  4299. static int
  4300. CharMeasureProc(chunkPtr, x)
  4301.     TkTextDispChunk *chunkPtr;        /* Chunk containing desired coord. */
  4302.     int x;                /* X-coordinate, in same coordinate
  4303.                      * system as chunkPtr->x. */
  4304. {
  4305.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4306.     int endX;
  4307.  
  4308.     return TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
  4309.         ciPtr->chars, chunkPtr->numChars-1, chunkPtr->x, x, 0,
  4310.         TK_IGNORE_TABS, &endX);
  4311. }
  4312.  
  4313. /*
  4314.  *--------------------------------------------------------------
  4315.  *
  4316.  * CharBboxProc --
  4317.  *
  4318.  *    This procedure is called to compute the bounding box of
  4319.  *    the area occupied by a single character.
  4320.  *
  4321.  * Results:
  4322.  *    There is no return value.  *xPtr and *yPtr are filled in
  4323.  *    with the coordinates of the upper left corner of the
  4324.  *    character, and *widthPtr and *heightPtr are filled in with
  4325.  *    the dimensions of the character in pixels.  Note:  not all
  4326.  *    of the returned bbox is necessarily visible on the screen
  4327.  *    (the rightmost part might be off-screen to the right,
  4328.  *    and the bottommost part might be off-screen to the bottom).
  4329.  *
  4330.  * Side effects:
  4331.  *    None.
  4332.  *
  4333.  *--------------------------------------------------------------
  4334.  */
  4335.  
  4336. static void
  4337. CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
  4338.     widthPtr, heightPtr)
  4339.     TkTextDispChunk *chunkPtr;        /* Chunk containing desired char. */
  4340.     int index;                /* Index of desired character within
  4341.                      * the chunk. */
  4342.     int y;                /* Topmost pixel in area allocated
  4343.                      * for this line. */
  4344.     int lineHeight;            /* Height of line, in pixels. */
  4345.     int baseline;            /* Location of line's baseline, in
  4346.                      * pixels measured down from y. */
  4347.     int *xPtr, *yPtr;            /* Gets filled in with coords of
  4348.                      * character's upper-left pixel. 
  4349.                      * X-coord is in same coordinate
  4350.                      * system as chunkPtr->x. */
  4351.     int *widthPtr;            /* Gets filled in with width of
  4352.                      * character, in pixels. */
  4353.     int *heightPtr;            /* Gets filled in with height of
  4354.                      * character, in pixels. */
  4355. {
  4356.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4357.     int maxX;
  4358.  
  4359.     maxX = chunkPtr->width + chunkPtr->x;
  4360.     TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
  4361.         ciPtr->chars, index, chunkPtr->x, 1000000, 0, TK_IGNORE_TABS,
  4362.         xPtr);
  4363.     if (index == ciPtr->numChars) {
  4364.     /*
  4365.      * This situation only happens if the last character in a line
  4366.      * is a space character, in which case it absorbs all of the
  4367.      * extra space in the line (see TkTextCharLayoutProc).
  4368.      */
  4369.  
  4370.     *widthPtr = maxX - *xPtr;
  4371.     } else if ((ciPtr->chars[index] == '\t')
  4372.         && (index == (ciPtr->numChars-1))) {
  4373.     /*
  4374.      * The desired character is a tab character that terminates a
  4375.      * chunk;  give it all the space left in the chunk.
  4376.      */
  4377.  
  4378.     *widthPtr = maxX - *xPtr;
  4379.     } else {
  4380.     TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
  4381.         ciPtr->chars + index, 1, *xPtr, 1000000, 0, TK_IGNORE_TABS,
  4382.         widthPtr);
  4383.     if (*widthPtr > maxX) {
  4384.         *widthPtr = maxX - *xPtr;
  4385.     } else {
  4386.         *widthPtr -= *xPtr;
  4387.     }
  4388.     }
  4389.     *yPtr = y + baseline - chunkPtr->minAscent;
  4390.     *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
  4391. }
  4392.  
  4393. /*
  4394.  *----------------------------------------------------------------------
  4395.  *
  4396.  * AdjustForTab --
  4397.  *
  4398.  *    This procedure is called to move a series of chunks right
  4399.  *    in order to align them with a tab stop.
  4400.  *
  4401.  * Results:
  4402.  *    None.
  4403.  *
  4404.  * Side effects:
  4405.  *    The width of chunkPtr gets adjusted so that it absorbs the
  4406.  *    extra space due to the tab.  The x locations in all the chunks
  4407.  *    after chunkPtr are adjusted rightward to align with the tab
  4408.  *    stop given by tabArrayPtr and index.
  4409.  *
  4410.  *----------------------------------------------------------------------
  4411.  */
  4412.  
  4413. static void
  4414. AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
  4415.     TkText *textPtr;            /* Information about the text widget as
  4416.                      * a whole. */
  4417.     TkTextTabArray *tabArrayPtr;    /* Information about the tab stops
  4418.                      * that apply to this line.  May be
  4419.                      * NULL to indicate default tabbing
  4420.                      * (every 8 chars). */
  4421.     int index;                /* Index of current tab stop. */
  4422.     TkTextDispChunk *chunkPtr;        /* Chunk whose last character is
  4423.                      * the tab;  the following chunks
  4424.                      * contain information to be shifted
  4425.                      * right. */
  4426.  
  4427. {
  4428.     int x, desired, delta, width, decimal, i, gotDigit;
  4429.     TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
  4430.     TkTextTab *tabPtr;
  4431.     CharInfo *ciPtr = NULL;        /* Initialization needed only to
  4432.                      * prevent compiler warnings. */
  4433.     int tabX, prev, spaceWidth;
  4434.     char *p;
  4435.     TkTextTabAlign alignment;
  4436.  
  4437.     if (chunkPtr->nextPtr == NULL) {
  4438.     /*
  4439.      * Nothing after the actual tab;  just return.
  4440.      */
  4441.  
  4442.     return;
  4443.     }
  4444.  
  4445.     /*
  4446.      * If no tab information has been given, do the usual thing:
  4447.      * round up to the next boundary of 8 average-sized characters.
  4448.      */
  4449.  
  4450.     x = chunkPtr->nextPtr->x;
  4451.     if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
  4452.     /*
  4453.      * No tab information has been given, so use the default
  4454.      * interpretation of tabs.
  4455.      */
  4456.  
  4457.     TkMeasureChars(textPtr->fontPtr, "\t", 1, x, INT_MAX, 0, 0, &desired);
  4458.     goto update;
  4459.     }
  4460.  
  4461.     if (index < tabArrayPtr->numTabs) {
  4462.     alignment = tabArrayPtr->tabs[index].alignment;
  4463.     tabX = tabArrayPtr->tabs[index].location;
  4464.     } else {
  4465.     /*
  4466.      * Ran out of tab stops;  compute a tab position by extrapolating
  4467.      * from the last two tab positions.
  4468.      */
  4469.  
  4470.     if (tabArrayPtr->numTabs > 1) {
  4471.         prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
  4472.     } else {
  4473.         prev = 0;
  4474.     }
  4475.     alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
  4476.     tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
  4477.         + (index + 1 - tabArrayPtr->numTabs)
  4478.         * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
  4479.     }
  4480.  
  4481.     tabPtr = &tabArrayPtr->tabs[index];
  4482.     if (alignment == LEFT) {
  4483.     desired = tabX;
  4484.     goto update;
  4485.     }
  4486.  
  4487.     if ((alignment == CENTER) || (alignment == RIGHT)) {
  4488.     /*
  4489.      * Compute the width of all the information in the tab group,
  4490.      * then use it to pick a desired location.
  4491.      */
  4492.  
  4493.     width = 0;
  4494.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4495.         chunkPtr2 = chunkPtr2->nextPtr) {
  4496.         width += chunkPtr2->width;
  4497.     }
  4498.     if (alignment == CENTER) {
  4499.         desired = tabX - width/2;
  4500.     } else {
  4501.         desired = tabX - width;
  4502.     }
  4503.     goto update;
  4504.     }
  4505.  
  4506.     /*
  4507.      * Must be numeric alignment.  Search through the text to be
  4508.      * tabbed, looking for the last , or . before the first character
  4509.      * that isn't a number, comma, period, or sign.
  4510.      */
  4511.  
  4512.     decimalChunkPtr = NULL;
  4513.     decimal = gotDigit = 0;
  4514.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4515.         chunkPtr2 = chunkPtr2->nextPtr) {
  4516.     if (chunkPtr2->displayProc != CharDisplayProc) {
  4517.         continue;
  4518.     }
  4519.     ciPtr = (CharInfo *) chunkPtr2->clientData;
  4520.     for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
  4521.         if (isdigit(UCHAR(*p))) {
  4522.         gotDigit = 1;
  4523.         } else if ((*p == '.') || (*p == ',')) {
  4524.         decimal = p-ciPtr->chars;
  4525.         decimalChunkPtr = chunkPtr2;
  4526.         } else if (gotDigit) {
  4527.         if (decimalChunkPtr == NULL) {
  4528.             decimal = p-ciPtr->chars;
  4529.             decimalChunkPtr = chunkPtr2;
  4530.         }
  4531.         goto endOfNumber;
  4532.         }
  4533.     }
  4534.     }
  4535.     endOfNumber:
  4536.     if (decimalChunkPtr != NULL) {
  4537.     int curX;
  4538.  
  4539.     ciPtr = (CharInfo *) decimalChunkPtr->clientData;
  4540.     TkMeasureChars(decimalChunkPtr->stylePtr->sValuePtr->fontPtr,
  4541.         ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0,
  4542.         TK_IGNORE_TABS, &curX);
  4543.     desired = tabX - (curX - x);
  4544.     goto update;
  4545.     } else {
  4546.     /*
  4547.      * There wasn't a decimal point.  Right justify the text.
  4548.      */
  4549.     
  4550.     width = 0;
  4551.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4552.         chunkPtr2 = chunkPtr2->nextPtr) {
  4553.         width += chunkPtr2->width;
  4554.     }
  4555.     desired = tabX - width;
  4556.     }
  4557.  
  4558.     /*
  4559.      * Shift all of the chunks to the right so that the left edge is
  4560.      * at the desired location, then expand the chunk containing the
  4561.      * tab.  Be sure that the tab occupies at least the width of a
  4562.      * space character.
  4563.      */
  4564.  
  4565.     update:
  4566.     delta = desired - x;
  4567.     TkMeasureChars(textPtr->fontPtr, " ", 1, 0, INT_MAX, 0, 0, &spaceWidth);
  4568.     if (delta < spaceWidth) {
  4569.     delta = spaceWidth;
  4570.     }
  4571.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4572.         chunkPtr2 = chunkPtr2->nextPtr) {
  4573.     chunkPtr2->x += delta;
  4574.     }
  4575.     chunkPtr->width += delta;
  4576. }
  4577.  
  4578. /*
  4579.  *----------------------------------------------------------------------
  4580.  *
  4581.  * SizeOfTab --
  4582.  *
  4583.  *    This returns an estimate of the amount of white space that will
  4584.  *    be consumed by a tab.
  4585.  *
  4586.  * Results:
  4587.  *    The return value is the minimum number of pixels that will
  4588.  *    be occupied by the index'th tab of tabArrayPtr, assuming that
  4589.  *    the current position on the line is x and the end of the
  4590.  *    line is maxX.  For numeric tabs, this is a conservative
  4591.  *    estimate.  The return value is always >= 0.
  4592.  *
  4593.  * Side effects:
  4594.  *    None.
  4595.  *
  4596.  *----------------------------------------------------------------------
  4597.  */
  4598.  
  4599. static int
  4600. SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
  4601.     TkText *textPtr;            /* Information about the text widget as
  4602.                      * a whole. */
  4603.     TkTextTabArray *tabArrayPtr;    /* Information about the tab stops
  4604.                      * that apply to this line.  NULL
  4605.                      * means use default tabbing (every
  4606.                      * 8 chars.) */
  4607.     int index;                /* Index of current tab stop. */
  4608.     int x;                /* Current x-location in line. Only
  4609.                      * used if tabArrayPtr == NULL. */
  4610.     int maxX;                /* X-location of pixel just past the
  4611.                      * right edge of the line. */
  4612. {
  4613.     int tabX, prev, result, spaceWidth;
  4614.     TkTextTabAlign alignment;
  4615.  
  4616.     if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
  4617.     TkMeasureChars(textPtr->fontPtr, "\t", 1, x, INT_MAX, 0, 0, &tabX);
  4618.     return tabX - x;
  4619.     }
  4620.     if (index < tabArrayPtr->numTabs) {
  4621.     tabX = tabArrayPtr->tabs[index].location;
  4622.     alignment = tabArrayPtr->tabs[index].alignment;
  4623.     } else {
  4624.     /*
  4625.      * Ran out of tab stops;  compute a tab position by extrapolating
  4626.      * from the last two tab positions.
  4627.      */
  4628.  
  4629.     if (tabArrayPtr->numTabs > 1) {
  4630.         prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
  4631.     } else {
  4632.         prev = 0;
  4633.     }
  4634.     tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
  4635.         + (index + 1 - tabArrayPtr->numTabs)
  4636.         * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
  4637.     alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
  4638.     }
  4639.     if (alignment == CENTER) {
  4640.     /*
  4641.      * Be very careful in the arithmetic below, because maxX may
  4642.      * be the largest positive number:  watch out for integer
  4643.      * overflow.
  4644.      */
  4645.  
  4646.     if ((maxX-tabX) < (tabX - x)) {
  4647.         result = (maxX - x) - 2*(maxX - tabX);
  4648.     } else {
  4649.         result = 0;
  4650.     }
  4651.     goto done;
  4652.     }
  4653.     if (alignment == RIGHT) {
  4654.     result = 0;
  4655.     goto done;
  4656.     }
  4657.  
  4658.     /*
  4659.      * Note: this treats NUMERIC alignment the same as LEFT
  4660.      * alignment, which is somewhat conservative.  However, it's
  4661.      * pretty tricky at this point to figure out exactly where
  4662.      * the damn decimal point will be.
  4663.      */
  4664.  
  4665.     if (tabX > x) {
  4666.     result = tabX - x;
  4667.     } else {
  4668.     result = 0;
  4669.     }
  4670.  
  4671.     done:
  4672.     TkMeasureChars(textPtr->fontPtr, " ", 1, 0, INT_MAX, 0, 0, &spaceWidth);
  4673.     if (result < spaceWidth) {
  4674.     result = spaceWidth;
  4675.     }
  4676.     return result;
  4677. }
  4678.